【FJOI2016】建筑师

题面

https://www.luogu.org/problem/P4609

题解:首先注意到整个“建筑群”被最高的建筑分成两部分,两部分相对独立。

把第一类斯特林数的环变成强制以最大值为头的序列,这样一个盒子代表了一个楼房和被它遮盖的楼房的集合。

所以只要有$a+b-2$个这样的集合,然后把其中的$a-1$个分给左侧,$b-1$个分给右侧就可以了。

为什么不组合数不乘在楼房数上?因为我只考虑了一个斯特林数。

  1. 第一类斯特林数的递推边界条件:$S1[0][0]=1$($i$,$j$都从$1$开始),我的写法是左小右大,在$i$>$j$时,$S1[i][j]=0$。
  2. 杨辉三角形处理组合数的时候,注意我通常的写法是左边大右边小的,如果调出来发现答案是$0$,可以看看是不是这里写错了。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mod 1000000007
#define LL long long 
#define ri register int

using namespace std;

inline int read() {
  int ret=0,f=0; char ch=getchar();
  while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar();
  while (ch>='0' && ch<='9') ret*=10,ret+=ch-'0',ch=getchar();
  return f?-ret:ret;
}

int T,n,a,b;
int s1[250][50500],c[250][250];

int main() {
  T=read();
  s1[0][0]=1;
  for (ri i=1;i<=230;i++)
    for (ri j=1;j<=50050;j++) s1[i][j]=(s1[i-1][j-1]+(s1[i][j-1]*1LL*(j-1))%mod)%mod;
  c[0][0]=1;
  for (ri i=1;i<=230;i++)
    for (ri j=0;j<=i+1;j++) c[i][j]=(((j==0)?0:c[i-1][j-1])+c[i-1][j])%mod;
  while (T--) {
    n=read(); a=read(); b=read();
    printf("%d\n",(s1[a+b-2][n-1]*1LL*c[a+b-2][a-1])%mod);
  }
  return 0;
}
posted @ 2019-09-03 00:01  HellPix  阅读(204)  评论(0编辑  收藏  举报