小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 nn 个建筑,每个建筑的高度是 11 到 nn 之间的一个整数。
小 Z 有很严重的强迫症,他不喜欢有两个建筑的高度相同。另外小 Z 觉得如果从最左边(所有建筑都在右边)看能看到 AA个建筑,从最右边(所有建筑都在左边)看能看到 BB 个建筑,这样的建筑群有着独特的美感。现在,小 Z 想知道满足上述所有条件的建筑方案有多少种?
如果建筑 ii 的左(右)边没有任何建造比它高,则建筑 ii 可以从左(右)边看到。两种方案不同,当且仅当存在某个建筑在两种方案下的高度不同。
输入格式:
第一行一个整数 TT ,代表 TT 组数据。 接下来 TT 行,每行三个整数 n,A,Bn,A,B 。
输出格式:
对于每组数据输出一行答案 \text{mod } 10^9+7mod 109+7 。
对于 10 \%10% 的数据 : 1 \leq n \leq 101≤n≤10 。
对于 20 \%20% 的数据 : 1 \leq n \leq 1001≤n≤100 。
对于 40 \%40% 的数据 : 1 \leq n \leq 50000, \ 1 \leq T \leq 51≤n≤50000, 1≤T≤5 。
对于 100 \%100% 的数据 : 1 \leq n \leq 50000, \ 1 \leq A, B \leq 100, \ 1 \leq T \leq 2000001≤n≤50000, 1≤A,B≤100, 1≤T≤200000 。
乍一看这不是以前在UVA做过的一道题么。。。。只不过数据范围无限扩大。。。。
一开始想看看能不能卡常卡过去。。。。于是就把那个题又写了一遍。。。但是跑了40+s才能跑出最大的数据。。。。
虽然很慢,但是 最基本的dp方程 也在这个程序里出现了,之后对程序的优化也需要用到这个转移的性质,所以还是先贴一下:
/*
f[i][j][k] -> f[i+1][j+1][k]
f[i][j][k] -> f[i+1][j][k+1]
f[i][j][k] * (i-1) -> f[i+1][j][k]
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define pb push_back
const int ha=1e9+7;
inline void add(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
struct node{ int num,A,B;};
vector<node> g[200005];
int f[2][105][105],T,n,X;
int Y,ans[200005],M,a,b;
inline void solve(){
f[0][1][1]=1;
for(int i=2,now=1,pre=0;i<=M;pre=now,now^=1,i++){
memset(f[now],0,sizeof(f[now]));
for(int j=0;j<=a;j++)
for(int k=0;k<=b;k++) if(f[pre][j][k]){
add(f[now][j][k],f[pre][j][k]*(ll)(i-2)%ha);
add(f[now][j+1][k],f[pre][j][k]);
add(f[now][j][k+1],f[pre][j][k]);
}
node x;
for(int j=g[i].size()-1;j>=0;j--){
x=g[i][j];
ans[x.num]=f[now][x.A][x.B];
}
}
}
int main(){
scanf("%d",&T);
for(int i=1;i<=T;i++){
scanf("%d%d%d",&n,&X,&Y);
g[n].pb((node){i,X,Y});
M=max(M,n),a=max(a,X),b=max(b,Y);
}
solve();
for(int i=1;i<=T;i++) printf("%d\n",ans[i]);
return 0;
}
但显然正解只能允许 50000 * 100的啦。。。。
我们考虑一下dp方程的转移,对于后两维,每次要么都不变(然后都乘上一个系数),或者两维之一+1。
所以怎么快速计算这个呢?? 我们考虑把后两位合并,最后再考虑两维+1顺序的问题。。。
也就是设 f[i][j] 为已经放了 i个桩子,左+右一共能看见j个的方案数(无序的)。
最后询问 A,B的时候 再乘上C(A+B-2,A-1) [枚举两维+1的顺序] 就行啦。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int ha=1e9+7;
inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
int f[50005][233],C[233][233],T,n,A,B;
inline void init(){
C[0][0]=1;
for(int i=1;i<=200;i++){
C[i][0]=1;
for(int j=1;j<=i;j++) C[i][j]=add(C[i-1][j-1],C[i-1][j]);
}
f[1][2]=1;
for(int i=1;i<50000;i++)
for(int j=2;j<=200;j++) if(f[i][j]){
ADD(f[i+1][j],f[i][j]*(ll)(i-1)%ha);
ADD(f[i+1][j+1],f[i][j]);
}
}
int main(){
init(),scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&A,&B);
printf("%d\n",f[n][A+B]*(ll)C[A+B-2][A-1]%ha);
}
return 0;
}
原文:https://www.cnblogs.com/JYYHH/p/9048111.html