又因为组成的数是 k 的倍数,且 k 比较小,那么需要 dp 中的一维,去枚举组成的数 % k 之后的余数。
那么 dp 方程为 dp [x][sta] 在状态为 sta 的时候组成的数 % k 的余数是 x 的方案数,
之后就是标准的状压 dp 的套路转移了!!!
代码
#include<bits/stdc++.h>
using namespace std;#definedbdouble#definelllonglong#definescscanf#defineprprintf#definefifirst#definesesecond#definepbpush_back#definem_pmake_pair#definePirpair<int,int>#defineinf0x3f3f3f3f#defineINF0x3f3f3f3f3f3f3f3f/*==========ACMer===========*/constint N =16;char s[N], t[100], num[1<< N];
ll pw[N +5][N +5];
ll dp[20][1<< N];voidpre_do(){for(int i ='0'; i <='9'; i ++)
t[i]= i -'0';for(int i ='A'; i <='F'; i ++)
t[i]= i -'A'+10;for(int i =0; i <(1<< N); i ++){for(int j =0; j < N; j ++){if(i &(1<< j))
num[i]++;}}for(int i =0; i <=16; i ++){
pw[i][0]=1;for(int j =1; j <=16; j ++)
pw[i][j]= pw[i][j -1]* i;}}intmain(){pre_do();int T, cas =1;sc("%d",&T);while(T --){int d, K;sc("%d %d %s",&d,&K, s);int n =strlen(s);int S =(1<< n)-1;memset(dp,0,sizeof dp);
dp[0][0]=1;for(int i =0; i <= S; i ++){for(int j =0; j < K; j ++){if(dp[j][i]==0)continue;for(int k =0; k < n; k ++){if(i &(1<< k))continue;
ll x =(pw[d][num[i]]% K * t[s[k]]+ j)% K;
dp[x][i |(1<< k)]+= dp[j][i];}}}pr("Case %d: %lld\n", cas ++, dp[0][S]);}return0;}
题目链接题意给我们一个 n 进制的数和一个数字 k,n <= 16, k <= 20, 这个 n 进制数中的每个进制为 的数字各不相同,现在我们可以重新给这个数字的每个进制位重新安排顺序,问有多少种方案数组成的数是 k 的倍数。题意一看到 n 很小,又是让求方案数,那肯定是状压 dp 了又因为组成的数是 k 的倍数,且 k 比较小,那么需要 dp 中的一维,去枚举组成的数 % k 之后的余数。那么 dp 方程为 dp [x][sta] 在状态为 sta 的时候组成的数