CF140E New Year Garland

题目

CF140E New Year Garland

分析

计数dp常规好题。

发现除了相邻两层的限制,我们对于每一层内部的计算是独立的。

于是可以考虑先计算所有内部的情况,再通过容斥干掉相邻两层不能相邻的限制(即没有限制的情况数-两层相同的情况数)。

对于每一层内部,我们考虑dp,设 (g[i][j]) 表示前 (i) 个小球,用了 (j) 种颜色的方案数。

转移很显然是:(g[i][j]=g[i-1][j-1]+g[i-1][j] imes dbinom {j-1}1)

含义为第 (i) 个小球可以是新的一种颜色,也可以是在之前已有的颜色当中选一个和上一个球不相同的颜色。

然后考虑层之间的方案数计数,还是考虑dp。

(f[i][j]) 表示前 (i) 层,第 (i) 层用了 (j) 个颜色的方案数。

转移方程也很容易得出:(f[i][j]=dbinom mjsumlimits_{k=1}^{min(l[i-1],m)}f[i-1][k] imes g[l[i]][j]-f[i-1][j] imes g[l[i]][j])

表示先不考虑限制,直接从前一个转移,减掉前一层和这一层相等的方案数。

然后我们还要记得颜色是有标号的,所以要全局加个标号:(f[i][j]=j! imes (dbinom mjsumlimits_{k=1}^{min(l[i-1],m)}f[i-1][k] imes g[l[i]][j]-f[i-1][j] imes g[l[i]][j]))

但是这样还没结束。

首先,这里的组合数不太好算,因为模数不固定,我们要算只能 (exlucas) ,但是发现可以把外面的 (j!) 丢进去变成排列数,而且还是总个数固定的排列数,这个计算要方便的多。

所以我们只需要预处理一下这些排列数和阶乘即可。

其次,我们发现这里的转移是 (O(l)) 的,会T掉,直接前缀和优化一下就行了。

接下来还会发现空间有点不够,但是我们知道 (f) 只会从前一个转移,而 (g) 是一直要用到的,所以可以把 (f) 滚动数组,并且排列数的那个数组很显然是一维的,因为总个数固定。

然后就差不多了。

代码

#include<bits/stdc++.h>
using namespace std;
//#ifdef ONLINE_JUDGE
//	#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//	char buf[1<<21],*p1=buf,*p2=buf;
//#endif
template<typename T>
inline void read(T &x){
	x=0;bool f=false;char ch=getchar();
	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
	while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template<typename T>
inline void write(T x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
#define ll long long
#define ull unsigned long long
#define ld long double
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define pc putchar
#define PII pair<int,int>
#define rep(i,x,y) for(register int i=(x);i<=(y);i++)
#define dep(i,y,x) for(register int i=(y);i>=(x);i--)
int MOD=100;
inline int inc(int x,int y){x+=y;return x>=MOD?x-MOD:x;}
inline int dec(int x,int y){x-=y;return x<0?x+MOD:x;}
inline void incc(int &x,int y){x+=y;if(x>=MOD) x-=MOD;}
inline void decc(int &x,int y){x-=y;if(x<0) x+=MOD;}
inline void chkmin(int &x,int y){if(y<x) x=y;}
inline void chkmax(int &x,int y){if(y>x) x=y;}
const int N=5005,M=1e6+5,INF=1e9+7;
int n,m,p,l[M],g[N][N],f[2][N],Am[N],fac[N],sum,tmp;
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	ios::sync_with_stdio(false);
//	double ST=clock();
	read(n),read(m),read(p);MOD=p;
	rep(i,1,n) read(l[i]);Am[0]=fac[0]=1;
	rep(i,1,5000) Am[i]=1ll*Am[i-1]*(m-i+1)%MOD,fac[i]=1ll*fac[i-1]*i%MOD;g[0][0]=1;
	rep(i,1,5000) rep(j,1,5000) g[i][j]=inc(g[i-1][j-1],1ll*g[i-1][j]*(j-1)%MOD);
	sum=1;
	rep(i,1,n){
		tmp=0;
		rep(j,l[i-1]+1,l[i]) f[(i&1)^1][j]=0;
		rep(j,1,l[i]) f[i&1][j]=dec(1ll*sum*g[l[i]][j]%MOD*Am[j]%MOD,1ll*f[(i&1)^1][j]*g[l[i]][j]%MOD*fac[j]%MOD),incc(tmp,f[i&1][j]);
		sum=tmp;
	}
	write(sum);
//#ifndef ONLINE_JUDGE
//	cerr<<"
Time:"<<(clock()-ST)/CLOCKS_PER_SEC<<"s
";
//#endif
	return 0;
}

原文地址:https://www.cnblogs.com/Akmaey/p/15477716.html