BZOJ1072[SCOI2007]排列perm 状压Dp

来源:互联网 发布:怎么在mac上把win删 编辑:程序博客网 时间:2024/05/21 06:56

题意:给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0)。例如123434有90种排列能
被2整除,其中末位为2的有30种,末位为4的有60种。

一开始看见s小于10秒想状压,然后在dp式子卡了一下才想到。。
设f[i][j]表示使用了多少位的状态,余数为j的方案数。
那么明显有f[i|(1<<k)][(j10+a[k])
但是这样肯定会算重,然后我就萎了。。其实很简单,就是/每个数出现的次数的阶乘就好了。。

#include<cstdio>#include<algorithm>#include<cstring>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;const int N=2e3+5;int f[N][N],a[N],cnt[N];int ans,n,m;char ch[20];int main(){    int cas;    scanf("%d",&cas);    while (cas--)    {        scanf("%s%d",ch,&n);        m=strlen(ch);        memset(cnt,0,sizeof(cnt));        memset(f,0,sizeof(f));        fo(i,0,m-1)        {            a[i]=ch[i]-'0';            ++cnt[a[i]];        }        f[0][0]=1;        int tot=1<<m;        fo(i,0,tot-1)        {            fo(j,0,n-1)            {                fo(k,0,m-1)                {                    if ((i&(1<<k))==0)                    f[i|(1<<k)][(j*10+a[k])%n]+=f[i][j];                 }            }        }ans=f[tot-1][0];        fo(i,0,9)        {            fo(j,1,cnt[i])ans/=j;        }        printf("%d\n",ans);    }}
原创粉丝点击