【计蒜客】「2017 计蒜之道 复赛」A.阿里云秘钥池 数位DP+莫比乌斯函数

来源:互联网 发布:证件拍照软件下载 编辑:程序博客网 时间:2024/06/06 14:28

内个……我还没有计蒜客的账号,就先不给题目传送门了……

看到数据范围不正常,就想到了数位DP。

定义f[i][j]表示从高到底DP到第i位,第i+1位上的数为j的方案数。

f[i][j]=k=1p1f[i1][k]×[(j,k)=1]=k=1p1f[i1][k]×d|(j,k)μ(d)=d|j(μ(d)×t=1p1df[i1][d×t])

当我们已知f[i1]时,可以用两次类似于埃氏筛的方法求出f[i],时间复杂度为O(plog2plogpr)=O(plog2r)

附上AC代码:

#include <cstdio>using namespace std;typedef long long ll;const int N=1e5+10;int t,mod,p[N],miu[N],num,len,a[70];ll f[70][N],l,r;bool b[N];inline void get(int n){    miu[1]=1;    for (int i=2; i<=n; ++i){        if (!b[i]) p[++num]=i,miu[i]=-1;        for (int j=1; j<=num&&p[j]*i<=n; ++j){            b[p[j]*i]=1,miu[p[j]*i]=-miu[i];            if (i%p[j]==0) {miu[p[j]*i]=0;break;}        }    }    return;}inline void change(ll x){    len=0;do a[++len]=x%mod,x/=mod; while (x);    return;}inline int gcd(int a,int b){return !b?a:gcd(b,a%b);}inline ll so(int x,int pre,bool b){    if (x==1) return 1ll;    if (!b&&f[x][pre]) return f[x][pre];    ll ans=0,lim=b?a[x-1]:mod-1;    for (int i=1; i<=lim; ++i) if (gcd(pre,i)==1) ans+=so(x-1,i,b&&(i==lim));    if (!b) f[x][pre]=ans;    return ans;}inline ll calc(ll x){    change(x);ll ans=0;    for (int i=1; i<len; ++i) for (int j=1; j<mod; ++j) ans+=f[i][j];    for (int i=1; i<=a[len]; ++i) ans+=so(len,i,i==a[len]);    return ans;}int main(void){    for (get(1e5),scanf("%d",&t); t; --t){        scanf("%lld%lld%d",&l,&r,&mod),change(r);        for (int i=0; i<=len; ++i) for (int j=0; j<mod; ++j) f[i][j]=0;        for (int i=1; i<mod; ++i) f[1][i]=1;        for (int i=2; i<=len; ++i)            for (int d=1; d<mod; ++d){                ll sum=0;                for (int t=d; t<mod; t+=d) sum+=f[i-1][t];                for (int t=d; t<mod; t+=d) f[i][t]+=sum*miu[d];            }        printf("%lld\n",calc(r)-calc(l-1));    }    return 0;}
阅读全文
0 0
原创粉丝点击