粘个题解。。。
100% 先考虑小问题:恰用 j 种颜色布置一行 i 个球的方案数 dp[i][j]。
用类似于最小表示法的思想,我们要求 x 号颜色的首次出现位置必须比 x+1
号颜色的早,这样一来将所求得的方案数乘以颜色的全排列数 j!就是原来的
方案数。若前 i-1 个球使用了 j-1 种颜色,则第 i 个球必然使用了第 j 种颜色;
若前 i-1 个球已使用了 j 种颜色,则第 i 个球使用的颜色必须与第 i-1 个球不
同,所以 有(j-1)种方案。故可由简单的 dp 递推而得:
F[i][j] = F[i-1][j-1] + F[i-1][j] * (j-1)
现在考虑相邻两层之间的限制,用 d[i][j]表示前 i 层中第 i 层恰用了 j 种颜色
的方案数。因为使用的颜色数不可能多于球数,又总球数不超过10^7,故d[i][j]
的状态总数也不超过 10^7。
若不考虑两层之间的颜色集合需不同这一条件,则转移方程为
d[i][j] = (m!/(m-j)!) * F[l[i]][j] * Σd[i-1][k]
再减去不符合这一条件的部分即可:
d[i][j] = (m!/(m-j)!) * F[l[i]][j] * Σd[i-1][k] – d[i-1][j] * F[l[i]][j] * j!
其中(m!/(m-j)!)与 j!都可以预处理得到
时间复杂度:O(Σl[i] + l[i]^2)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int n,m,p,cnt,tot,mx; 6 long long sum,tmp,ml; 7 int l[1000005]; 8 long long mul[1000005]; 9 long long f[5005][5005]; 10 long long pr[1000005]; 11 long long dp[1000005]; 12 void pre_work(){ 13 mul[m+1]=1ll; 14 for(int i=m;i>=1;i--) 15 mul[i]=mul[i+1]*1ll*i%p; 16 } 17 int main(){ 18 scanf("%d%d%d",&n,&m,&p); 19 pre_work(); 20 for(int i=1;i<=n;i++){ 21 scanf("%d",&l[i]); 22 mx=max(l[i],mx); 23 }f[0][0]=1; 24 for(int i=1;i<=mx;i++) 25 for(int j=1;j<=min(i,m);j++) 26 f[i][j]=(f[i-1][j-1]+f[i-1][j]*1ll*(j-1))%p; 27 tmp=1; 28 for(int i=1;i<=n;i++){ 29 sum=tmp;tmp=0;ml=1; 30 for(int j=1;j<=l[i];j++){ 31 ml*=1ll*j;ml%=p; 32 dp[j]=((sum*mul[m-j+1]%p*f[l[i]][j]%p-pr[j]*f[l[i]][j]%p*ml%p)%p+p)%p; 33 (tmp+=dp[j])%=p; 34 } 35 for(int j=1;j<=l[i-1];j++)pr[j]=0; 36 for(int j=1;j<=l[i];j++)pr[j]=dp[j]; 37 }printf("%I64d ",tmp); 38 return 0; 39 }