[FJOI2016] 建筑师
Posted 蒟蒻JHY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[FJOI2016] 建筑师相关的知识,希望对你有一定的参考价值。
题目描述
小 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; }
以上是关于[FJOI2016] 建筑师的主要内容,如果未能解决你的问题,请参考以下文章