题目链接:戳这里
题目大意:输入一个数,将该数的每个数位重新排列后(无前导零),有多少个数%m=0?
分析:因为最多有18位数,可以考虑用18位的2进制数表示每一位上的数是否使用,状压dp转移即可。
定义dp[state][k]表示当前状态为state时,%m余数为j的方案数。
转移方程为dp[state][(k*10+dight[j])%m]=dp[state^(1<<j)][k]。
注意重复问题,如果一个数字出现多次,假设出现x次,那么会对答案造成x!倍的重复,在dp转移完后要去重。
最终答案为dp[1<<len-1][0]。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL read()
{
char c;LL sum=0,f=1;c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
LL n,m;
LL dp[1<<18][105];
int dight[20],len,num[10];
LL a[20];
int main()
{
n=read();m=read();
while(n)
{
int x=n%10;
dight[len++]=x;
num[x]++;
n/=10;
}
sort(dight,dight+n);
for(int i=0;i<len;i++)
if(dight[i])
dp[1<<i][dight[i]%m]=1;
for(int i=0;i<(1<<len);i++)
for(int j=0;j<len;j++)
if(i&(1<<j))
for(int k=0;k<m;k++)
dp[i][(k*10+dight[j])%m]+=dp[i^(1<<j)][k];
a[0]=1;
for(int i=1;i<=len;i++)
a[i]=a[i-1]*i;
for(int i=0;i<10;i++) dp[(1<<len)-1][0]/=a[num[i]];
printf("%I64d\n",dp[(1<<len)-1][0]);
return 0;
}