BZOJ 4665: 小w的喜糖

Sol

DP+容斥.

这就是一个错排的扩展...可是想到容斥却仅限于种数的容斥,如果种数在一定范围内我就会做了QAQ.

但是容斥的是一定在原来位置的个数.

发现他与原来的位置无关,可以先把每个同种的糖看成本质不同的来DP.

\(f[i][j]\) 表示前 \(i\) 种糖,一定有 \(j\) 个不合法的方案数.

然后在第 \(i\) 种内枚举有几种不合法的状态.这样就可以从前 \(i-1\) 种转移了.

然后随便搞搞组合数,还有线性计算逆元,上篇随笔里提到了.

Code

/**************************************************************
    Problem: 4665
    User: BeiYu
    Language: C++
    Result: Accepted
    Time:2548 ms
    Memory:32720 kb
****************************************************************/
 
#include<cstdio>
#include<iostream>
using namespace std;
  
const int N = 2005;
const int Mo = 1000000009;
typedef long long LL;
  
int n,m;LL ans;
int a[N],fac[N],inv[N],C[N][N];
int f[N][N];
  
inline int in(int x=0,char ch=getchar()){ while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x; }
int main(){
//  freopen("in.in","r",stdin);
//  freopen("out.out","w",stdout);
      
    n=in(),fac[0]=fac[1]=1,C[0][0]=1,f[0][0]=1;
      
    inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++) inv[i]=(LL)(Mo-Mo/i)*inv[Mo%i]%Mo,fac[i]=(LL)fac[i-1]*i%Mo;
//  for(int i=1;i<=n;i++) cout<<inv[i]<<" ";cout<<endl;
    for(int i=2;i<=n;i++) inv[i]=(LL)inv[i-1]*inv[i]%Mo;
    for(int i=1;i<=n;i++) for(int j=0;j<=i;j++) if(!j) C[i][j]=1;else C[i][j]=(LL)(C[i-1][j-1]+C[i-1][j])%Mo;
      
    for(int i=1;i<=n;i++) a[in()]++;
    for(int i=1;i<=n;i++) if(a[i]) a[++m]=a[i];
     
    for(int i=1;i<=m;i++) for(int j=0;j<=n;j++) for(int k=0;k<=a[i]&&k<=j;k++)
        f[i][j]=(LL)(f[i][j]+(LL)f[i-1][j-k]*C[a[i]][k]%Mo*fac[a[i]]%Mo*inv[a[i]-k])%Mo;
      
    for(int i=0;i<=n;i++) if(i&1) ans=(ans-(LL)f[m][i]*fac[n-i]%Mo+Mo)%Mo;else ans=(ans+(LL)f[m][i]*fac[n-i]%Mo)%Mo;
    for(int i=1;i<=m;i++) ans=ans*inv[a[i]]%Mo;
    cout<<ans<<endl;
//  for(int i=1;i<=n;i++) for(int j=0;j<=n;j++) printf("%d%c",f[i][j]," \n"[j==n]);
    return 0;
}

  

posted @ 2016-09-18 15:37  北北北北屿  阅读(191)  评论(0编辑  收藏  举报