CodeForces 55D Beautiful numbers (数位DP+数论+打表规律)

题目链接:http://codeforces.com/problemset/problem/55/D

题目大意

给定L和R,求这个区间中满足条件的数
的个数,条件为:该数可以整除每一位上非零的数。

题目分析 

这道题我看题解思路的,
刚接触数位DP,我觉得这道题应该是利用数论技巧来
化简状态的题目。、
首先分析状态,数位DP其状态的延展方式可能不方便用
循环描述所以直接记忆化,关键是如恶化找到关于数位的转移方程,
我们不难利用数论的简单性质,我们只要保存当前扩展到的每一位的最小公倍数
就可以判定对于当前的数字是否符合要求了,
我们还要再保存当前数字的大小,这也是刚开始困扰我的地方,
数字太大无法保存。看了题解才知道,1到9的最小公倍数是2520不算太大,
所以可以通过保存当前数值对2520的模数来化简状态,因为我们答案的要求的特殊性,
所以减去任意个2520是无影响的,这个思想很巧妙~
然后1到9其所有的公倍数打表发现重复的很多,
全保存下来直接二分映射下即可,这样状态就减少到20*2520*50了,
由于记忆化搜索的原因时间复杂度合理了。

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

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define ll unsigned long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define mst(a,b) memset((a),(b),sizeof(a))
#define pii pair<int,int>
#define fi first
#define se second
#define mk(x,y) make_pair(x,y)
const int maxn=3e3+5;
const int ub=2520;
const int N=1<<9;
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int lcm(int x,int y){return x/gcd(x,y)*y;}
/*
题目大意:
给定L和R,求这个区间中满足条件的数
的个数,条件为:该数可以整除每一位上非零的数。

题目分析:
这道题我看题解思路的,
刚接触数位DP,我觉得这道题应该是利用数论技巧来
化简状态的题目。、
首先分析状态,数位DP其状态的延展方式可能不方便用
循环描述所以直接记忆化,关键是如恶化找到关于数位的转移方程,
我们不难利用数论的简单性质,我们只要保存当前扩展到的每一位的最小公倍数
就可以判定对于当前的数字是否符合要求了,
我们还要再保存当前数字的大小,这也是刚开始困扰我的地方,
数字太大无法保存。看了题解才知道,1到9的最小公倍数是2520不算太大,
所以可以通过保存当前数值对2520的模数来化简状态,因为我们答案的要求的特殊性,
所以减去任意个2520是无影响的,这个思想很巧妙~
然后1到9其所有的公倍数打表发现重复的很多,
全保存下来直接二分映射下即可,这样状态就减少到20*2520*50了,
由于记忆化搜索的原因时间复杂度合理了。
*/
int t;
ll l,r,f[30][maxn][60],ans;
int dig[30],P[1<<9],p=0,cnt=0;
int bound(int x)
{
    int l=0,r=p,mid;
    while(l+1!=r)
    {
        mid=(l+r)>>1;
        if(P[mid]>x)r=mid;
        else l=mid;
    }
    return l;
}
ll dfs(int pos,int mod,int pre,int limit){
    if(pos<0){
        if(mod%P[pre]) return 0;
        else return 1;
    }
    if(!limit&&f[pos][mod][pre]!=-1)  return f[pos][mod][pre];
    ll ret=0;
    int last=limit?dig[pos]:9,val;
    rep(i,0,last+1){
        int tm=((mod<<3)+(mod<<1)+i)%ub;
        int pe=pre;
        if(i) pe=bound(lcm(i,P[pre]));
        ret+=dfs(pos-1,tm,pe,limit&&(i==last));
    }
    if(!limit) f[pos][mod][pre]=ret;
    return ret;
}
ll solve(ll x){
    cnt=0;
    while(x) dig[cnt++]=x%10,x/=10;
    return dfs(cnt-1,0,0,1);
}
int main(){
    rep(i,1,N){
        P[i-1]=1;
        rep(j,0,9) if(i&(1<<j))
            P[i-1]=lcm(P[i-1],j+1);
    }
    sort(P,P+N-1);
    p=unique(P,P+N-1)-P;
    mst(f,-1);
    scanf("%d",&t);
    while(t--){
        ll a,b;
        scanf("%I64d%I64d",&a,&b);
        printf("%I64d\n",solve(b)-solve(a-1));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37451344/article/details/88728912