codeforces 401D Roman and Numbers

题目简述:

给定n和m,求把n的各位数字重排后有多少数能被m整除(禁止有前导0的数)

n (1 ≤ n < 1018) and m (1 ≤ m ≤ 100).

本来是打算做一道数位DP的 然后看到了这题 也往数位DP上想 结果……

------------------------------------------------------------------------------------------------------

还是来说正解吧 正解是状压DP

我们可以用$f[i][j]$表示已用的数字集合为$i$,对$m$求余为$j$的方案数

每次往一个较小的集合所构成的数末尾添加一个数字得到一个较大的集合(这样前导0更方便处理)

然后注意不同位上的相同数字是等价的 于是最后还要除一下所有数字出现次数的阶乘

#include <bits/stdc++.h>
using namespace std;
long long f[1<<18][100];
int num[18],pos[1<<18],cnt[10];
long long n;
int m,top;
int main()
{
    scanf("%lld%d",&n,&m);
    int tmp=1;
    while(n)
    {
        cnt[n%10]++;
        pos[tmp]=n%10;
        num[top++]=n%10;
        n/=10;
        tmp<<=1;
    }
    for(int i=1;i<(1<<top);++i)
        if(i==(i&-i))
            f[i][pos[i]%m]=(pos[i]!=0);
        else
        {
            for(int j=0;j<top;++j)
                if(i&(1<<j))
                    for(int k=0;k<m;++k)
                        f[i][(k*10+num[j])%m]+=f[i^(1<<j)][k];
        }
    long long ans=f[(1<<top)-1][0];
    for(int i=0;i<=9;++i)
        for(int j=1;j<=cnt[i];++j)
            ans/=j;
    printf("%lld",ans);
    return 0;
}

 

posted @ 2015-08-21 17:35  sagitta  阅读(268)  评论(0编辑  收藏  举报