1. 程式人生 > >洛谷 CF55D Beautiful numbers 解題報告

洛谷 CF55D Beautiful numbers 解題報告

main can dfs clas ron gcd 解題報告 const return

CF55D Beautiful numbers

題意

\(t(\le 10)\)次詢問區間\([l,r](1\le l\le r\le 9\times 10^{18})\)中能被每一位上數整除的數的個數,0算可以整除任何數。


最開始寫了個假做法,既沒算0復雜度也是假的

最開始把每個模數都壓進去了,後來發現只壓2520就可以了

然後前兩位壓前\(i\)位與\(\bmod 2520\)的結果,第三位最開始壓了每個數字出現集合,這樣就很屑。

可以直接壓數字集合的最小公倍數,僅有不到50個取值,做一個Hash存一下就可以了。

寫起來也簡單


Code:

#include <cstdio>
#include <cstring>
#define ll long long
const int p=2520;
ll dp[20][p][50];
int yuy[p],bit[20],po[20],cnt;
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int LCM(int x,int y){return y?x*y/gcd(x,y):x;}
ll dfs(int pos,int res,int lcm,int lim)
{
    if(!pos) return res%lcm==0;
    if(!lim&&~dp[pos][res][yuy[lcm]]) return dp[pos][res][yuy[lcm]];
    ll ret=0;
    for(int i=0,up=lim?bit[pos]:9;i<=up;i++)
        ret+=dfs(pos-1,(res+po[pos-1]*i)%p,LCM(lcm,i),lim&&i==up);
    return lim?ret:dp[pos][res][yuy[lcm]]=ret;
}
ll cal(ll x)
{
    int len=0;while(x) bit[++len]=x%10,x/=10;
    return dfs(len,0,1,1);
}
int main()
{
    memset(dp,-1,sizeof dp);
    int T;scanf("%d",&T);
    for(int i=1;i<p;i++)
        if(p%i==0)
            yuy[i]=++cnt;
    po[0]=1;for(int i=1;i<=18;i++) po[i]=po[i-1]*10%p;
    while(T--)
    {
        ll l,r;scanf("%lld%lld",&l,&r);
        printf("%lld\n",cal(r)-cal(l-1));
    }
    return 0;
}

2019.2.10

洛谷 CF55D Beautiful numbers 解題報告