【BZOJ 4665】 4665: 小w的喜糖 (DP+容斥)

4665: 小w的喜糖

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 94  Solved: 53

Description

废话不多说,反正小w要发喜糖啦!!
小w一共买了n块喜糖,发给了n个人,每个喜糖有一个种类。这时,小w突发奇想,如果这n个人相互交换手中的糖,那会有多少种方案使得每个人手中的糖的种类都与原来不同。
两个方案不同当且仅当,存在一个人,他手中的糖的种类在两个方案中不一样。

Input

第一行,一个整数n
接下来n行,每行一个整数,第i个整数Ai表示开始时第i个人手中的糖的种类
对于所有数据,1≤Ai≤k,k<=N,N<=2000

Output

一行,一个整数Ans,表示方案数模1000000009

Sample Input

6
1
1
2
2
3
3

Sample Output

10

HINT

Source

【分析】

  DP+容斥类的题目都不是很会做啊QWQ。

  人不分顺序,同一种糖果要一起做。

  f[i][j]表示前i种糖果,至少有j个人的是不合法的方案数。

  f[i][j]=f[i-1][j-k]*c[a[i]][k]*(a[i]*(a[i]-1)*(a[i]-2)**[乘k次])

  最后把其他的全排列f[n][i]=f[n][i]*(n-i)!

  然后容斥,if(i&1) ans-=f[n][i] else ans+=f[n][i];

  然后ans/(a[i]!)

  先把所有糖果都看成不一样的,最后除以每种糖果的数量的阶乘,就能保证本质不同了。

  ORZ Claris。。

  学会了不用全部开LL的方法了,在前面加一个1LL* 然后每次乘完就Mod

  中间用到了线性求逆元,复习一下:ny[i]=(Mod-Mod/i)*ny[Mod%i]%Mod

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 #define Mod 1000000009
 8 #define Maxn 2010
 9 #define LL long long
10 
11 int a[Maxn],c[Maxn][Maxn],pw[Maxn],ny[Maxn];
12 int f[Maxn][Maxn];
13 
14 int main()
15 {
16     int n;
17     scanf("%d",&n);
18     memset(a,0,sizeof(a));
19     for(int i=1;i<=n;i++)
20     {
21         int x;
22         scanf("%d",&x);
23         a[x]++;
24     }
25     for(int i=0;i<=n;i++) c[i][0]=1;
26     for(int i=1;i<=n;i++)
27      for(int j=1;j<=i;j++)
28      {
29          c[i][j]=(c[i-1][j-1]+c[i-1][j])%Mod;
30      }
31     int ans=0;
32     pw[0]=1;
33     for(int i=1;i<=n;i++) pw[i]=1LL*pw[i-1]*i%Mod;
34     ny[0]=ny[1]=1;
35     for(int i=2;i<=n;i++) ny[i]=1LL*(Mod-Mod/i)*ny[Mod%i]%Mod;
36     for(int i=2;i<=n;i++) ny[i]=1LL*ny[i]*ny[i-1]%Mod;
37     memset(f,0,sizeof(f));
38     f[0][0]=1;
39     for(int i=1;i<=n;i++)
40      for(int j=0;j<=n;j++)
41       for(int k=0;k<=a[i];k++)
42       {
43           if(k>j) break;
44           f[i][j]=(f[i][j]+1LL*f[i-1][j-k]*c[a[i]][k]%Mod*pw[a[i]]%Mod*ny[a[i]-k]%Mod)%Mod;
45       }
46     for(int i=0;i<=n;i++)
47     {
48         if(i&1) ans-=1LL*f[n][i]*pw[n-i]%Mod;
49         else ans+=1LL*f[n][i]*pw[n-i]%Mod;
50         ans=(ans%Mod+Mod)%Mod;
51     }
52     for(int i=1;i<=n;i++) if(a[i]) ans=(1LL*ans*ny[a[i]])%Mod;
53     printf("%d
",ans);
54     return 0;
55 }
View Code

2017-04-11 14:25:31

原文地址:https://www.cnblogs.com/Konjakmoyu/p/6693391.html