【62.89%】【BZOJ 1072】[SCOI2007]排列perm
Submit: 1862 Solved: 1171
[Submit][Status][Discuss]
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
000 1
001 1
1234567890 1
123434 2
1234 7
12345 17
12345678 29
Sample Output
1
3
3628800
90
3
6
1398
3
3628800
90
3
6
1398
HINT
在前三个例子中,排列分别有1, 3, 3628800种,它们都是1的倍数。
【限制】
100%的数据满足:s的长度不超过10, 1<=d<=1000, 1<=T<=15
【题解】
设f[i][j]表示哪些数字被按顺序放在了前面。i的范围是0..2^10-1.
j是按照数字放的顺序组成的十进制数mod d的结果。
然后我们从小到大枚举状态i。
然后看看状态i有哪个数字没有被按顺序放下去。
假设t这个数字没有被放下去。
就把t这个数字接在前面已经放好顺序的数字后面。
根据同余律(numpre*10 + t) %k == 把t接在前面的数字后面组成的数字 % k;
然后比如这样的
序列 1 3 5 5 6
它的不重复排列为 A(5,5)/A(2,2);
1 3 3 5 5 6
它的不重复排列为A(6,6)/(A(2,2)*A(2,2));
所以最后得到的f[2^len - 1][0] 要除(chongfu[i]!) ,i∈[0..9]
【代码】
#include <cstdio> #include <cstring> int t; char s[15]; int d,num[15],a[15],len; int f[1 << 11][1010],two_n[11],fac[11]; int main() { //freopen("F:\\rush.txt", "r", stdin); two_n[0] = 1; for (int i = 1; i <= 10; i++) two_n[i] = two_n[i - 1] * 2;//预处理出2^x fac[0] = 1; for (int i = 1; i <= 10; i++)//处理出n! fac[i] = fac[i - 1] * i; scanf("%d", &t); while (t--) { memset(num, 0, sizeof(num)); memset(f, 0, sizeof(f)); scanf("%s", s); scanf("%d", &d); len = strlen(s); for (int i = 1; i <= len; i++) { a[i] = s[i - 1] - '0'; num[a[i]]++; } f[0][0] = 1; for (int i = 0; i <= two_n[len]; i++) for (int k = 0;k <= d-1;k++) for (int j = 1;j <= len;j++) if ((i & two_n[j - 1])== 0) f[i | two_n[j - 1]][(k * 10 + a[j]) % d] += f[i][k];//或运算对于把这个数字加到后面 for (int i = 0; i <= 9; i++) f[two_n[len] - 1][0] /= fac[num[i]]; printf("%d\n", f[two_n[len] - 1][0]); } return 0; }