• 省选前集训 状压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
    ",ans);
    71     return 0;
    72 }
  • 相关阅读:
    20165336 2017-2018-2《Java程序设计》课程总结
    2017-2018-2 20165336 实验五《网络编程与安全》实验报告
    2017-2018-2 20165336 实验四《Android开发基础》实验报告
    2018-2019-1 20165314 《信息安全系统设计基础》第四周学习总结
    2018-2019-1 20165314 《信息安全系统设计基础》第三周学习总结
    2018-2019-1 20165314《信息安全系统设计基础》实验一 缓冲区溢出漏洞实验
    20165314 [第二届构建之法论坛] 预培训心得(Java版)
    20165314 2017-2018-2《Java程序设计》课程总结
    20165314实验五《网络编程与安全》实验报告
    20165314实验四
  • 原文地址:https://www.cnblogs.com/zqq123/p/5248664.html
Copyright © 2020-2023  润新知