Loading

P4609 [FJOI2016]建筑师

P4609 [FJOI2016]建筑师

首先注意到 \(n\) 这个高度很特殊,因为它是左边看到的最后一个建筑,也是右边看到的最后一个建筑,也就是说,它是分界点。

\(n\) 左边有 \(A-1\) 分割点,每一段都是递减的,最高的那个是开头,也就是分割点。

这样就能满足从左往右看到 \(A\) 个高度了。

右边同理。

发现我们可以直接用第一类斯特林数的定义了!!!

答案就是把 \(n-1\) 划分成 \(A+B-2\) 个循环的方案,其中 \(n\) 因为没法动被去除了。

因为对于每一个循环只有一种高度排列合法(即最高的放最后或者最前),而循环也恰好会被统计一次,也就是说这两个东西是一一对应的。

\(A+B-2\) 个循环还要选 \(A-1\) 个放到 \(n\) 左边,所以还得乘组合数。

那么答案就是

\[\begin{bmatrix}n-1\\A+B-2\end{bmatrix}\binom{A+B-2}{A-1} \]

所以这题唯一的考点对于第一类斯特林数的熟练程度。

int S1[50005][205],fac[205],ifc[205];
#define mod 1000000007
inline int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
int comb(int n,int m){return n<m?0:1ll*fac[n]*1ll*ifc[m]%mod*ifc[n-m]%mod;}
void init(){
	fac[0]=1;rep(i,1,200)fac[i]=1ll*fac[i-1]*i%mod;
	ifc[200]=qpow(fac[200],mod-2);per(i,199,0)ifc[i]=1ll*ifc[i+1]*(i+1)%mod;
	S1[0][0]=1;rep(i,1,50000)rep(j,1,200)S1[i][j]=(S1[i-1][j-1]+1ll*S1[i-1][j]*(i-1)%mod)%mod;
}
signed main(){
	init();
	for(int T=read();T;--T){
		int n=read(),A=read(),B=read();
		if(A+B-1>n)puts("0");
		else printf("%lld\n",1ll*comb(A+B-2,A-1)*S1[n-1][A+B-2]%mod);
	}
	return 0;
}
posted @ 2021-01-13 19:05  zzctommy  阅读(143)  评论(0编辑  收藏  举报