BZOJ 1072 排列perm (状压dp)

来源:互联网 发布:算法分析专业 编辑:程序博客网 时间:2024/06/15 16:34

1072: [SCOI2007]排列perm

Time Limit: 10 Sec Memory Limit: 128 MB

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

思路:
s的长度不超过10,算法就大概确定了。
f[i][j]表示选取i状态的数且此时的mod为j。
对于每个状态i,可能从它把的某一个二进制位置为1的位置变为零的状态转移过来(当前选了x个数有a种排列方式使得mod为k,那么在剩下的数字之中任选一个加在排列的最后,那么选x+1个数使得mod为k*10+这个数的值,就多了a种排列方式。代码注释中会解释。),这样不是特别容易操作,所以考虑逆向更新。
dp方程:f[(i|(1<<(k−1)))][(j∗10+a[k])%mod]+=f[i][j];条件是( i&(1<<(k-1)) )==0;
但是如果有重复的数字的话,我们会重复计算,所以除掉重复的数的个数的排列。

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 15#define M 1100using namespace std;int mod, lens, lim;int a[N], cnt[N];char s[N];int f[1<<10][M];//f[i][j]表示选取i状态的数且此时的mod为j//(eg:i=5时,选第1和第3个数,i=9时,选第1和第4个数,i=10时,选第2和第4个数) int J[]={0,1,2,6,24,120,720,5040,40320,362880,3628800};//阶乘 void dfs(){    for(int i=0; i<lim; i++)        for(int k=0; k<mod; k++)            if( f[i][k] )                for(int j=0; j<lens; j++)                    if( (i & (1<<j)) == 0 )                        f[(i|(1<<j))][(k*10+a[j+1])%mod] += f[i][k];//a[j+1]放在最后     //对于状态i,排列的最后一位可能是选取了的数中的任意一个(前面的排列已经统计好了)    //为什么只是加在最后而不是其他地方,因为加在其他地方的情况会在加上它的最后一位的计算中被统计 }int main(){    int T;scanf("%d", &T);    while( T-- ){        memset(cnt, 0, sizeof(cnt));        memset(f, 0, sizeof(f));        scanf("%s", s);        lens = strlen(s);        for(int i=0; i<lens; i++){            a[i+1] = s[i] - '0';            cnt[a[i+1]]++;        }        scanf("%d", &mod);        lim = (1<<lens);        f[0][0] = 1;        dfs();        int ans = f[lim-1][0];//所有数字用完         for(int i=0; i<=9; i++) if( cnt[i] ) ans /= J[cnt[i]];//相同的数字调换位置本质不变         printf("%d\n", ans);    }    return 0;}
阅读全文
0 0
原创粉丝点击