[FJOI2016]建筑师
Description:
小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 \(n\) 个建筑,每个建筑的高度是 \(1\) 到 \(n\) 之间的一个整数。
小 Z 有很严重的强迫症,他不喜欢有两个建筑的高度相同。另外小 Z 觉得如果从最左边(所有建筑都在右边)看能看到 \(A\) 个建筑,从最右边(所有建筑都在左边)看能看到 \(B\) 个建筑,这样的建筑群有着独特的美感。现在,小 Z 想知道满足上述所有条件的建筑方案有多少种?
Hint:
\(T \le 2*10^5 ,n \le 5*10^4 ,A,B \le 100\)
Solution:
只会暴力dp......
如果熟悉斯特林数,这题应该很好想吧
我们把被看到的建筑及它其挡住的建筑看做整体
除去最高的一座,有\(A-1\)座在左边,\(B-1\)座在右边
再枚举左右
答案就是\(S(n-1,A+B-2)*C(A+B-2,A-1)\)
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1
#define rs p<<1|1
using namespace std;
typedef long long ll;
const ll mxn=5e4,mod=1e9+7;
ll n,m,T,cnt,hd[mxn],fac[mxn+5],ifac[mxn+5];
ll s[mxn+5][205];
inline ll read() {
char c=getchar(); ll x=0,f=1;
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
return x*f;
}
inline void chkmax(ll &x,ll y) {if(x<y) x=y;}
inline void chkmin(ll &x,ll y) {if(x>y) x=y;}
struct ed {
ll to,nxt;
}t[mxn<<1];
inline void add(ll u,ll v) {
t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}
ll qpow(ll a,ll b) {
ll res=1,bs=a;
while(b) {
if(b&1) res=1ll*res*bs%mod;
bs=1ll*bs*bs%mod;
b>>=1;
}
return res;
}
ll C(ll n,ll m) {
return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int main()
{
T=read(); fac[0]=ifac[0]=1; ll n,A,B; s[0][0]=s[1][1]=1;
for(ll i=1;i<=mxn;++i) fac[i]=1ll*fac[i-1]*i%mod;
ifac[mxn]=qpow(fac[mxn],mod-2);
for(ll i=mxn-1;i>=1;--i) ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
for(ll i=2;i<=mxn;++i) s[i][1]=1ll*s[i-1][1]*(i-1)%mod;
for(ll i=2;i<=mxn;++i)
for(ll j=1;j<=i&&j<=200;++j)
s[i][j]=(s[i-1][j-1]+1ll*s[i-1][j]*(i-1)%mod)%mod;
while(T--) {
n=read(); A=read(); B=read();
printf("%lld\n",1ll*s[n-1][A+B-2]*C(A+B-2,A-1)%mod);
}
return 0;
}