Codeforces 708E. Student's Camp 题解

题目链接:E. Student's Camp

题目大意:洛谷


题解:首先考虑最朴素的转移即设 (f_{i,l,r}) 表示到了第 (i) 层,前面全部连通,第 (i) 层剩余的砖块是 ([l,r]) 的方案数,然后考虑容斥转移,令 (P_{l,r}) 表示一行中恰好剩下 ([l,r]) 中的砖块的概率,然后就可以得到(f_{i,l,r}=P_{l,r}(sum f_{i-1,l^{'},r^{'}}-sum_{r^{'}<l} f_{i-1,l^{'},r_{'}}-sum_{l_{'}>r}f_{i-1,l^{'},r^{'}}))

然后把整个式子整理一下,因为容易发现后面的两个式子是对称的,所以我们可以将第三个式子翻转一下变成和第二个式子一样的形式,再设 (s_{i,r}=sum_{l<=r}f_{i,l,r}) , (f_i=sum f_{i,l,r}=sum s_{i,r}) ,然后上面的转移就可以全部用 (s)(f)·来表示,然后再将整个式子用 (s,f) 来表示,来推出仅和 (s,f) 有关的递推式,然后再考虑用前缀和优化一下,这样的话时间复杂度就变成了(O(nm))的。

代码:

#include <cstdio>
int quick_power(int a,int b,int Mod){
	int ans=1;
	while(b){
		if(b&1){
			ans=1ll*ans*a%Mod;
		}
		b>>=1;
		a=1ll*a*a%Mod;
	}
	return ans;
}
int min(int a,int b){
	return a<b?a:b;
}
const int Maxn=1500;
const int Maxk=100000;
const int Mod=1000000007;
int n,m;
int p;
int k;
int frac[Maxk+5],inv_f[Maxk+5];
int g[Maxn+5],q[Maxn+5];
void init(){
	frac[0]=1;
	for(int i=1;i<=Maxk;i++){
		frac[i]=1ll*frac[i-1]*i%Mod;
	}
	inv_f[Maxk]=quick_power(frac[Maxk],Mod-2,Mod);
	for(int i=Maxk-1;i>=0;i--){
		inv_f[i]=1ll*inv_f[i+1]*(i+1)%Mod;
	}
}
int C(int n,int m){
	return 1ll*frac[n]*inv_f[m]%Mod*inv_f[n-m]%Mod;
}
int f[Maxn+5][Maxn+5],s[Maxn+5][Maxn+5],h[Maxn+5][Maxn+5];
int main(){
	init();
	scanf("%d%d",&n,&m);
	scanf("%d%d",&p,&k);
	p=1ll*p*quick_power(k,Mod-2,Mod)%Mod;
	scanf("%d",&k);
	for(int i=0;i<=min(m,k);i++){
		g[i]=1ll*C(k,i)*quick_power(p,i,Mod)%Mod*quick_power((1-p+Mod)%Mod,k-i,Mod)%Mod;
	}
	for(int i=1;i<=m;i++){
		q[i]=(q[i-1]+g[i-1])%Mod;
	}
	f[0][m]=s[0][m]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			f[i][j]=1ll*g[m-j]*((1ll*(s[i-1][m]-s[i-1][m-j]+Mod)%Mod)*q[j]%Mod-h[i-1][j]+Mod)%Mod;
			s[i][j]=(s[i][j-1]+f[i][j])%Mod;
			h[i][j]=(h[i][j-1]+1ll*g[j-1]*s[i][j-1])%Mod;
		}
	}
	printf("%d
",s[n][m]);
	return 0;
}
原文地址:https://www.cnblogs.com/withhope/p/13611582.html