果然一遇到dp就瞬间失去了思考能力。。
整没看出来这是状压dp,思路也很不错。
我们可以发现s长度很短,所以很容易想到状压来枚举,我们枚举一些数字是否使用,用状压记录一下,也就是设f[s][i]表示状态为s的膜d为i的方案数,然后通过枚举下一位转移。
怎么转移呢?
我们枚举了模数所以新添的一位就可以直接加上枚举的模数再膜就是新添一位后的新膜数f[i^(1<<(k-1))][(j*10+s[k]-'0')%d]+=f[i][j]。
注意:我们这里算的是组合并不是排列,所以要再除一个全排列就ok了。
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int times[10],f[1<<11][1005];
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
int d,n;
char s[15];
scanf("%s%d",s+1,&d);
n=strlen(s+1);
for(int i=0;i<=9;i++)
times[i]=0;
for(int i=0;i<=(1<<10);i++)
for(int j=0;j<=1005;j++)
f[i][j]=0;
for(int i=1;i<=n;i++)
times[s[i]-'0']++;
f[0][0]=1;
for(int i=0;i<(1<<n);i++)
for(int j=0;j<d;j++)
for(int k=1;k<=n;k++)
if(!(i&(1<<(k-1))))
f[i^(1<<(k-1))][(j*10+s[k]-'0')%d]+=f[i][j];
for (int i=0;i<=9;i++)
for (int j=2;j<=times[i];j++)
f[(1<<n)-1][0]/=j;
printf("%d\n",f[(1<<n)-1][0]);
}
return 0;
}