• bzoj1072[SCOI2007]排列perm


    传送门

    Description

      给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0)。例如123434有90种排列能
    被2整除,其中末位为2的有30种,末位为4的有60种。

    Input

      输入第一行是一个整数T,表示测试数据的个数,以下每行一组s和d,中间用空格隔开。s保证只包含数字0, 1
    , 2, 3, 4, 5, 6, 7, 8, 9.

    Output

      每个数据仅一行,表示能被d整除的排列的个数。

    Sample Input

    7
    000 1
    001 1
    1234567890 1
    123434 2
    1234 7
    12345 17
    12345678 29

    Sample Output

    1
    3
    3628800
    90
    3
    6
    1398

    HINT

    在前三个例子中,排列分别有1, 3, 3628800种,它们都是1的倍数。

    【限制】

    100%的数据满足:s的长度不超过10, 1<=d<=1000, 1<=T<=15

    题解

    这道题的做法不就是枚举全排列然后暴力哈希判断吗/滑稽

    (以上做法纯属口胡)

    我们一看到字符串的长度小于等于十,就很自然地想到要么是搜索,要么是状压。显然这道题去写搜索全排列的话复杂度一定会爆炸。因此我们考虑采用状压dp。我们定义f[i][j]表示当前状态为i,余数为j的情况有多少种,其中i是一个二进制数。二进制下i的第k位为1表示原来排列中的第k个数已经被使用过了,否则就没有使用过。对于转移,我们考虑每一次选择一个还没有使用过的原本在第j位的数x,将其作为最后一位,那么之后的状态就是(i|(1<<(j-1))),若原来的余数为y,那么新的余数必定为((y*10+x)%d)。其中d在题目中已经给出。因此我们就可以把f[i][j]转移到f[i|(1<<(k-1))][(j*10+a[k])%d]。其中k表示选择作为最后一位的数原来的位置,a[k]表示选择作为最后一位的数。即

    f[i|(1<<(k-1))][(j*10+a[k])%d]=f[i|(1<<(k-1))][(j*10+a[k])%d]+f[i][j]。

    代码

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<algorithm>
     6 #include<cmath>
     7 using namespace std;
     8 int dp[(1<<10)+10][1010];
     9 int tp[20],a[20],re[20],va[20];
    10 int len,t,d;
    11 char ch[20];
    12 int main(){
    13     int i,j,k;tp[0]=1;
    14     for(i=1;i<=10;++i)  tp[i]=(tp[i-1]<<1);
    15     scanf("%d",&t);
    16     for(int cas=1;cas<=t;++cas){
    17         scanf("%s",ch);
    18         scanf("%d",&d);
    19         len=strlen(ch);
    20         for(i=0;i<=9;++i){
    21             re[i]=0;va[i]=1;
    22         }
    23         for(i=0;i<len;++i){
    24             a[i]=ch[i]-48;
    25             re[a[i]]++;va[a[i]]=va[a[i]]*re[a[i]];
    26         }
    27         for(i=0;i<tp[len];++i){
    28             for(j=0;j<d;++j)  dp[i][j]=0;
    29         }
    30         dp[0][0]=1;
    31         for(i=0;i<tp[len];++i){
    32             for(j=0;j<d;++j){
    33                 if(dp[i][j]){
    34                     for(k=0;k<len;++k){
    35                         if(!(tp[k]&i)){
    36                             dp[i|tp[k]][(j*10+a[k])%d]+=dp[i][j];
    37                         }
    38                     }
    39                 }
    40             }
    41         }
    42         for(i=0;i<=9;++i)  dp[tp[len]-1][0]/=va[i];
    43         printf("%d
    ",dp[tp[len]-1][0]);
    44     }
    45     return 0;
    46 }
  • 相关阅读:
    Python
    Python
    Python
    Python
    Python
    Python
    Python
    python
    对象
    py常用模块
  • 原文地址:https://www.cnblogs.com/lazytear/p/9175475.html
Copyright © 2020-2023  润新知