bzoj 1072 状压DP
我们用w[i][j]来表示,i是一个二进制表示我们选取了s中的某些位,j表示这些位%d为j,w[i][j]则表示这样情况下的方案数,那么我们可以得到转移.w[i|(1<<k)][(j*10+s[k]-'0')%d]+=w[i][j]。
假设s中有x个3,那么我们算出的状态中同样的数我们算了x!次,最后除掉就好了。
/************************************************************** Problem: 1072 User: BLADEVIL Language: C++ Result: Accepted Time:476 ms Memory:12680 kb ****************************************************************/ //By BLADEVIL #include <cstdio> #include <cstring> using namespace std; int d,cnt[11],w[3010][1010]; char s[11]; int main() { int task; scanf("%d",&task); while (task--) { scanf("%s%d",s,&d); int len=strlen(s); memset(cnt,0,sizeof cnt); for (int i=0;i<len;i++) cnt[s[i]-'0']++; for (int i=0;i<(1<<len);i++) for (int j=0;j<d;j++) w[i][j]=0; w[0][0]=1; for (int i=0;i<(1<<len);i++) for (int j=0;j<d;j++) if (w[i][j]) for (int k=0;k<len;k++) if (!(i&(1<<k))) w[i|(1<<k)][(j*10+s[k]-'0')%d]+=w[i][j]; int ans=w[(1<<len)-1][0]; //printf("%d\n",ans); for (int i=0;i<10;i++) for (int j=1;j<=cnt[i];j++) { ans/=j ; } printf("%d\n",ans); } return 0; }