【FJOI2016】建筑师
问题描述
小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 n 个建筑,每个建筑的高度是 1 到 n 之间的一个整数。
小 Z 有很严重的强迫症,他不喜欢有两个建筑的高度相同。另外小 Z 觉得如果从最左边(所有建筑都在右边)看能看到 A个建筑,从最右边(所有建筑都在左边)看能看到 B 个建筑,这样的建筑群有着独特的美感。现在,小 Z 想知道满足上述所有条件的建筑方案有多少种?
如果建筑 i 的左(右)边没有任何建造比它高,则建筑 i 可以从左(右)边看到。两种方案不同,当且仅当存在某个建筑在两种方案下的高度不同。
输入格式
第一行一个整数 T,代表 T 组数据。 接下来 T 行,每行三个整数 n,A,B
输出格式
对于每组数据输出一行答案 mod10^9+7。
样例输入 1
2 3 2 2 3 1 2
样例输出 1
2 1
样例输入 2
5 1 1 1 2 1 1 4 3 1 10 2 2 8 6 4
样例输出 2
1 0 3 219168 0
提示
对于 10% 的数据 : 1≤n≤10 对于 20% 的数据 : 1≤n≤100 对于 40% 的数据 : 1≤n≤50000, 1≤T≤5 对于 100%的数据 :1≤n≤50000, 1≤A,B≤100, 1≤T≤200000
显然最高的那个一定看得见放在哪里都一样,我们不管它。
然后考虑最高的杆子左边的部分(右边同理)
每 两个看得见的杆子之间都可能会有被挡住的杆子,我们把一个看得见的杆子和它挡住的杆子看成一个集合,假设这个集合大小为C,那么在确定集合元素的情况下, 这个集合的排列的方案数实际上就是C个元素圆排列的方案数。因为一个圆排列都对应且仅对应一个合法排列(因为最高的杆子肯定在最左边所以肯定是从最高的杆 子这里断开),所以问题就变成了把n-1个杆子分成a+b-2个圆排列的方案数(因为最高的杆子左边有a-1个这样的集合,右边有b-1个),就 是$S(n-1,a+b-2)$。当然对于每种分法我们要确定这个圆排列是在左边还是在右边,所以要乘上$C(a+b-2,a-1)$。
答案就是$S(n-1,a+b-2)*C(a+b-2,a-1)$,预处理一下$O(1)$回答就好了。
1 #include2 #include 3 #define rep(i,l,r) for (int i=l; i<=r; i++) 4 using namespace std; 5 6 const int MOD=1000000007,M=210,N=50010; 7 int s[N][M],C[M][M],T,n,a,b; 8 9 void init(int n,int a) { 10 rep(i,0,n){ 11 s[i][0]=0; 12 if(i<=a) s[i][i]=1; 13 rep(j,1,min(i,a)) s[i][j]=(0ll+s[i-1][j-1]+1ll*(i-1)*s[i-1][j]%MOD)%MOD; 14 } 15 rep(i,0,a){ 16 C[i][0]=1; 17 rep(j,1,i) C[i][j]=(0ll+C[i-1][j]+C[i-1][j-1])%MOD; 18 } 19 } 20 21 int main() { 22 init(50000,200); 23 for (scanf("%d",&T); T--; ) 24 scanf("%d%d%d",&n,&a,&b),printf("%lld\n",(1ll*s[n-1][a+b-2]*C[a+b-2][a-1])%MOD); 25 return 0; 26 }