Description

给定 \([l,r]\),求 \([l,r]\) 之间有多少个数满足它的任意数位都可以整除它本身。

Solution

\(f(i,j,k,full)\) 表示从高到低考虑到第 \(i\) 位数,前面构成的数模 \(2520=j\),前面这些数位的 \(LCM\)\(k\),当前是否贴合上界的方案数。把 \(k\) 离散化一下节约空间。

注意只需要记录 full=0 的情况即可,并且这些情况可以在各组数据间复用,因此就要求 \(i\) 要倒着记录。

#include <bits/stdc++.h>
using namespace std;

#define int long long 
const int N = 1000005;


vector <int> a;
int mp[2525],imp[2525];
int f[20][2520][50],n;
int gg[55][55];
int CNT;

int gcd(int p,int q)
{
    p=mp[p];
    q=mp[q];
    if(!(~gg[p][q]))
    {
        ++CNT;
        return gg[p][q]=__gcd(imp[p],imp[q]);
    }
    else 
    {
        return gg[p][q];
    }
}

int lcm(int p,int q)
{
    if(p==0) return q;
    if(q==0) return p;
    return p*q/gcd(p,q);
}


int calc(int i,int j,int k,int full)
{
    CNT++;
    if(i<0) return j%imp[k]==0; 
    if(!full && ~f[i][j][k]) return f[i][j][k];
    int lim=full?a[i]:9;
    int ans=0;

    for(int now=0;now<=lim;now++)
    {
        int newj=(j*10+now)%2520;
        int newk=mp[lcm(now,imp[k])];
        ans+=calc(i-1,newj,newk,full&&now==lim);
    }
    if(!full) return f[i][j][k]=ans;
    else return ans;
}

int solve(int n)
{
    int ans=0;
    a.clear();
    // memset(f,-1,sizeof f);
    while(n)
    {
        a.push_back(n%10);
        n/=10;
    }
    ::n=a.size();
    if(a.size()==0) return 1;
    // reverse(a.begin(),a.end());
    int tmp = calc(::n-1,0,1,1);
    return tmp;
}

void doit()
{
    int l,r;
    cin>>l>>r;
    cout<<solve(r)-solve(l-1)<<endl;
}

signed main()
{
    
    ios::sync_with_stdio(false);
    
    int ind=0;
    for(int i=1;i<=2520;i++)
    {
        if(2520%i==0)
        {
            mp[i]=++ind;
            imp[ind]=i;
        }
    }
    memset(gg,-1,sizeof gg);
    memset(f,-1,sizeof f);
    int t;
    cin>>t;
    while(t--)
    {
        doit();
    }

    // cout<<CNT<<endl;
}

相关文章: