[dp][组合数] Jzoj P6303 演员
题解
- 设f[i][j][k]表示填完前i位,出现过j中颜色,后面还可以填k种颜色的方案数
- 那么就会有两种转移:①填新的颜色f[i+1][j+1][k+1] ②填一种k中的颜色,填了这种颜色还能继续填f[i+1][j][k],不能继续填f[i+1][j][k-1]
- 可以证明块最多只会有2m个,那就设[0/1]表示可以继续填的k种颜色中,最后一种是否出现在最后一位
- 最后用组合数算算答案即可
代码
1 #include <cstdio> 2 #include <iostream> 3 #define ll long long 4 using namespace std; 5 const ll N=310,M=1e6,mo=1e9+7; 6 ll n,m,r,ans,fac[M*2],inv[M*2],f[N*2][N][N],g[N*2][N][N]; 7 ll ksm(ll a,ll b) { for (r=1;b;b>>=1,a=a*a%mo) if (b&1) r=r*a%mo; return r; } 8 ll C(ll a,ll b) { return fac[a]*inv[b]%mo*inv[a-b]%mo; } 9 int main() 10 { 11 freopen("actor.in","r",stdin),freopen("actor.out","w",stdout),scanf("%lld%lld",&n,&m),fac[0]=1; 12 for (int i=1;i<=M;i++) fac[i]=fac[i-1]*i%mo; 13 inv[M]=ksm(fac[M],mo-2); 14 for (int i=M-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mo; 15 f[0][0][0]=1,ans=0; 16 for (int i=1;i<=min(2*m,n);i++) for (int j=1;j<=m;j++) for (int k=j,sum=0;k;k--) 17 f[i][j][k]=(f[i-1][j-1][k-1]+g[i-1][j][k+1])%mo,g[i][j][k]=(g[i][j][k+1]+f[i][j][k])%mo,ans=(ans+f[i][j][k]*C(m,j)%mo*fac[j]%mo*C(n-1,i-1)%mo)%mo; 18 printf("%lld",ans); 19 }