当前位置: 代码迷 >> 综合 >> CodeForces 55D Beautiful numbers (数位DP+数论+打表规律)
  详细解决方案

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

热度:93   发布时间:2023-11-15 11:59:24.0

题目链接: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;
}