CF140E New Year Garland
题目
分析
计数dp常规好题。
发现除了相邻两层的限制,我们对于每一层内部的计算是独立的。
于是可以考虑先计算所有内部的情况,再通过容斥干掉相邻两层不能相邻的限制(即没有限制的情况数-两层相同的情况数)。
对于每一层内部,我们考虑dp,设 \(g[i][j]\) 表示前 \(i\) 个小球,用了 \(j\) 种颜色的方案数。
转移很显然是:\(g[i][j]=g[i-1][j-1]+g[i-1][j]\times \dbinom {j-1}1\)
含义为第 \(i\) 个小球可以是新的一种颜色,也可以是在之前已有的颜色当中选一个和上一个球不相同的颜色。
然后考虑层之间的方案数计数,还是考虑dp。
设 \(f[i][j]\) 表示前 \(i\) 层,第 \(i\) 层用了 \(j\) 个颜色的方案数。
转移方程也很容易得出:\(f[i][j]=\dbinom mj\sum\limits_{k=1}^{\min(l[i-1],m)}f[i-1][k]\times g[l[i]][j]-f[i-1][j]\times g[l[i]][j]\)
表示先不考虑限制,直接从前一个转移,减掉前一层和这一层相等的方案数。
然后我们还要记得颜色是有标号的,所以要全局加个标号:\(f[i][j]=j!\times (\dbinom mj\sum\limits_{k=1}^{\min(l[i-1],m)}f[i-1][k]\times g[l[i]][j]-f[i-1][j]\times 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<<"\nTime:"<<(clock()-ST)/CLOCKS_PER_SEC<<"s\n";
//#endif
return 0;
}