【YBTOJ】【数位DP】 魔法数字
魔法数字
还是很麻烦的一道题……
题解
先证明一个结论:
若 \(p=\operatorname{lcm}(p_1,p_2,\cdots,p_n)\) ,则有\(\forall x\in \N ,i\in[1,n], (x\operatorname{mod} p)\operatorname{mod} p_i = x\operatorname{mod}p_i\) .
证明:
因为\(p=\operatorname{lcm}(p_1,p_2,\cdots,p_n)\),则有\(p = k\cdot p_i\).
则\(x=s\cdot p+r = (k\cdot s)p_i+r\)
证毕。
应用:对于 \(\forall x\) ,求 \(x\operatorname{mod}\{p_{1\cdots n}\}\) ,可以求出 \(x \operatorname{mod} \operatorname{lcm}(p)\) ,再求其模。
对于此题:要求此数对于 \(1\cdots9\) 的余数,可以求其与 \(\operatorname{lcm}(1\cdots 9)\) 的余数,再单独取余。
题目中还要求是否出现过某数,可以使用状压来解决。
则dp状态:
\(dp(pos,st,lft)\) 表示:
- 填到第 \(pos\) 位.
- 是否出现过某数的状态为 \(st\) .
- 取余后结果为 \(lft\) .
如果这样设计的话,空间复杂度是\(O(18\times 512\times 2520) \approx O(2e7)\).
考虑如何优化这个过程:
如果末位是\(0\)或\(5\),则它一定是\(5\)的倍数。
这样,就不需要考虑\(5\)的影响。其\(\operatorname{lcm}\)变为\(504\).
代码
#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 1e5+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
ll ret=0;char ch=' ',c=getchar();
while(!(c>='0'&&c<='9'))ch=c,c=getchar();
while(c>='0'&&c<='9')ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
return ch=='-'?-ret:ret;
}
ll l,r;int k;
ll dp[20][520][510];
int num[20];
ll dfs(int pos,int st,int lft,short lst,bool lim){
// printf(" dfs(%d)\n",pos);
if(!pos){
// printf(" ans add(%d,%d,%d,%d)\n",st,lft,lst,lim);
int cnt = 0;
for(int i = 1 ; i <= 9 ; i ++)
if(i != 5 && st & (1<<(i-1)) && !(lft % i)) cnt ++;
if(st & (1<<4) && (lst == 5 || !lst)) cnt ++;
// puts(cnt >= k ? "YES" : "NO");
return cnt >= k;
}
if(~dp[pos][st][lft] && !lim) return dp[pos][st][lft];
int mx = lim ? num[pos] : 9;
ll ret = 0;
for(int i = 0 ; i <= mx ; i ++)
ret += dfs(pos-1,!i ? st : st | (1<<(i-1)) , (lft*10+i)%504 , i , lim && i==mx);
if(!lim) dp[pos][st][lft] = ret;
return ret;
}
inline ll solve(ll x){
// printf(" ans(%lld) = ",x);
int cnt = 0;
while(x)
num[++cnt] = x % 10,
x /= 10;
// printf("%lld\n",dfs(cnt,0,0,0,1));
// puts("");puts("");;
return dfs(cnt,0,0,0,1);
}
signed main(){
memset(dp,-1,sizeof(dp));
k = read() , l = read() , r = read();
printf("%lld",solve(r)-solve(l-1));
return 0;
}