Lightoj1021Painful Bases (状压DP)

来源:互联网 发布:手机淘宝客采集软件 编辑:程序博客网 时间:2024/06/13 22:43
1021 - Painful Bases
PDF (English)StatisticsForum
Time Limit: 2 second(s)Memory Limit: 32 MB

As you know that sometimes base conversion is a painful task. But still there are interesting facts in bases.

For convenience let's assume that we are dealing with the bases from 2 to 16. The valid symbols are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E and F. And you can assume that all the numbers given in this problem are valid. For example 67AB is not a valid number of base 11, since the allowed digits for base 11 are 0 to A.

Now in this problem you are given a base, an integer K and a valid number in the base which contains distinct digits. You have to find the number of permutations of the given number which are divisible by KK is given in decimal.

For this problem, you can assume that numbers with leading zeroes are allowed. So, 096 is a valid integer.

Input

Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case starts with a blank line. After that there will be two integers, base (2 ≤ base ≤ 16) and K (1 ≤ K ≤ 20). The next line contains a valid integer in that base which contains distinct digits, that means in that number no digit occurs more than once.

Output

For each case, print the case number and the desired result.

Sample Input

Output for Sample Input

3

 

2 2

10

 

10 2

5681

 

16 1

ABCDEF0123456789

Case 1: 1

Case 2: 12

Case 3: 20922789888000

 

题目链接:http://lightoj.com/volume_showproblem.php?problem=1021

题目大意:

base表示base进制,给一个k(0 <= k <= 20),给一个base进制下的合法数,保证每位数字都不同,求这个数字的所有排列中是k的倍数的个数



思路:


容易想到状态dp[n][S][m](S是数字出现的集合),表示前n位用了数字集S且模k余数是m的方案数。

利用 (xy)base % k = ( x*base+y ) % k = (( x%k ) * base + y) % k ,进行状态第三维的转移。

不过d[16][216][20]有2000多W的状态数,且不说超时的问题,内存早已超过限制了。

可以发现,S这一维其实就包含了n这一维的信息了,所以只要二维就能表示状态。dp[m][S]表示,数字集为s且模k余数为m的方案数状态的转移,从前往后不好处理余数,所以我从后往前更新状态的值,所谓的“我为人人”:

从dp[m][S]出发,更新dp[(m*base+y)%k][S'],其中S'-S={y}。

#include<bits/stdc++.h>using namespace std;typedef long long ll;ll dp[20][(1<<16)];int symbol[128];int main(){    int t, base, mod;    char str[20];    for(int i = 0; i < 10; i++) symbol[i+'0'] = i;    for(int i = 10; i < 16; i++) symbol[i-10+'A'] = i;    scanf("%d", &t);    for(int kase = 1; kase <= t; kase++)    {        scanf("%d%d%s", &base, &mod, str);        int n = strlen(str);        memset(dp, 0, sizeof(dp));        dp[0][0] = 1;        for(int i = 0; i < (1<<n); i++)            for(int j = 0; j < mod; j++)                for(int k = 0; k < n; k++)                {                    if((i>>k) & 1) continue;                    dp[(j*base+symbol[str[k]]) % mod][i|(1<<k)] += dp[j][i];                }        printf("Case %d: %lld\n", kase, dp[0][(1<<n)-1]);    }    return 0;}