传送门
状态:f[i][j]表示状态为i,余数为j的排列数。
转移:f[i|(1<<(j-1))][(k*10+a[j])%d]+=f[i][k];其中j不包含在状态i里。
目标:f[tot][0];
注意:这里处理有重复元素的全排列的方法:将所有的重复元素的全排列求出来,然后直接求整个的全排列,然后分别除以每一个重复元素的全排列。这样做是显然正确,因为每一种重复元素在整个里的贡献就是它自己的全排列,我们只保留一个。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=10;
const int D=1005;
char s[N];
int T,d,n,tot;
int a[N+1],f[1<<N][D],cnt[N+1],v[N+1];
inline void clear(){
d=n=tot=0; memset(a,0,sizeof(a)); memset(f,0,sizeof(f));memset(cnt,0,sizeof(cnt));
for (int i=0;i<=9;++i) v[i]=1;
}
int main(){
scanf("%d",&T);
while (T--){
clear();
cin>>s>>d; n=strlen(s); for (int i=1;i<=n;++i) a[i]=s[i-1]-'0';
tot=(1<<n)-1;
for (int i=1;i<=n;++i)
cnt[a[i]]++,v[a[i]]*=cnt[a[i]];
f[0][0]=1;
for (int i=0;i<=tot;++i)
for (int k=0;k<d;++k)
if (f[i][k])
for (int j=1;j<=n;++j)
if ((i&(1<<(j-1)))==0)
f[i|(1<<(j-1))][(k*10+a[j])%d]+=f[i][k];
for (int i=0;i<=9;++i) f[tot][0]/=v[i];
printf("%d\n",f[tot][0]);
}
}
刚开始的思路是错的,因为我无法存下所有排列的状态。
然后看了黄学长的思路:一维是余数。并且借鉴了解决重复元素问题的方法。
十分巧妙啊。