【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;
} 
posted @ 2021-09-13 10:18  Last-Order  阅读(67)  评论(0编辑  收藏  举报