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\n",dp[tp[len]-1][0]);
44     }
45     return 0;
46 }

 

posted @ 2018-06-12 22:51  lazytear  阅读(142)  评论(0编辑  收藏  举报