省选前集训 状压dp(容斥原理)

Kykneion asma

Time limit :1000ms

On the last day before the famous mathematician Swan's death, he left a problem to the world: Given integers n and ai for 0≤i≤4, calculate the number of n-digit integers which have at most ai-digit i in its decimal representation (and have no 5,6,7,8 or 9). Leading zeros are not allowed in this problem.

 

直接统计较为困难

考虑容斥:所有情况-至少一个超出+至少两个超出-.....

然后就可以状压dp了。dp[i][j]表示第i为状态为j,从低向高dp,每次可以把已经超出了的和不在意的任意填。或是枚举当前已超出的为在哪超出。乘组合数转移。

注意容斥思想的应用,直接统计较难时,先统计一部分,在减去重了的

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define maxn 20020
 6 #define mod 1000000007
 7 
 8 typedef long long LL;
 9 LL dp[maxn][50];
10 LL fac[maxn],inv[maxn],cnt[maxn];
11 int ans;
12 int n,a[5];
13 
14 inline int power(LL x,int y){
15     LL res = 1;
16     while ( y ){
17         if ( y & 1 ) res = (res * x) % mod;
18         x = (x * x) % mod;
19         y >>= 1;
20     }
21     return res % mod;
22 }
23 void init(){
24     fac[0] = 1;
25     for (int i = 1 ; i <= n ; i++){
26         fac[i] = (fac[i - 1] * (LL) i) % mod;;
27         inv[i] = power(fac[i],mod - 2);
28     }
29     for (int i = 1 ; i < 32 ; i++){
30         int now = i;
31         while ( now ) {if ( now & 1 ) cnt[i]++; now >>= 1;}
32     //    cout<<cnt[i]<<" ";
33     }
34     //cout<<endl;
35 }
36 inline LL C(int n,int m){
37     if ( n == m || m == 0 ) return 1;
38     if ( m > n ) return 0;
39     return (fac[n] * inv[m] % mod * inv[n - m] % mod) % mod;
40 }
41 int doDp(){
42     int ans = power(5,n - 1);
43     for (int i = 1 ; i <= 5 ; i++){ //枚举状态中必须超出的数量
44         memset(dp,0,sizeof(dp));
45         dp[0][0] = 1;
46         for (int j = 1 ; j <= n - 1 ; j++){
47             for (int k = 0 ; k < 32 ; k++){
48                 if ( cnt[k] > i ) continue;
49                 dp[j][k] = (dp[j][k] + (dp[j - 1][k] * (LL) (5 - i + cnt[k]))) % mod;
50                 for (int l = 0 ; l <= 4 ; l++){
51                     if ( ((k >> l) & 1) && (j > a[l]) ) 
52                         dp[j][k] = (dp[j][k] + dp[j - a[l] - 1][(1 << l) ^ k] * C(j - 1,a[l])) % mod;
53                 }
54             }
55         }
56         if ( i & 1 ){
57             for (int j = 0 ; j < 32 ; j++) if ( i == cnt[j] ) ans = (int) ((LL)ans - dp[n - 1][j]) % mod;
58         }
59         else{
60             for (int j = 0 ; j < 32 ; j++) if ( i == cnt[j] ) ans = (int) ((LL)ans + dp[n - 1][j]) % mod;
61         }
62     }
63     return (ans % mod + mod) % mod; 
64 }
65 int main(){
66     scanf("%d",&n);
67     for (int i = 0 ; i <= 4 ; i++) scanf("%d",&a[i]);
68     init();
69     for (int i = 1 ; i <= 4 ; i++) if ( a[i] ) a[i]-- , ans = (ans + doDp()) % mod , a[i]++;//cout<<ans<<endl;
70     printf("%d\n",ans);
71     return 0;
72 }

 

你可能感兴趣的:(省选前集训 状压dp(容斥原理))