【YBTOJ】【数位DP】 魔法数字

魔法数字

还是很麻烦的一道题……

题解

先证明一个结论:

(p=operatorname{lcm}(p_1,p_2,cdots,p_n)) ,则有(forall xin N ,iin[1,n], (xoperatorname{mod} p)operatorname{mod} p_i = xoperatorname{mod}p_i) .

证明:

因为(p=operatorname{lcm}(p_1,p_2,cdots,p_n)),则有(p = kcdot p_i).

(x=scdot p+r = (kcdot s)p_i+r)

证毕。

应用:对于 (forall x) ,求 (xoperatorname{mod}{p_{1cdots n}}) ,可以求出 (x operatorname{mod} operatorname{lcm}(p)) ,再求其模。

对于此题:要求此数对于 (1cdots9) 的余数,可以求其与 (operatorname{lcm}(1cdots 9)) 的余数,再单独取余。

题目中还要求是否出现过某数,可以使用状压来解决。

则dp状态:

(dp(pos,st,lft)) 表示:

  • 填到第 (pos) 位.
  • 是否出现过某数的状态为 (st) .
  • 取余后结果为 (lft) .

如果这样设计的话,空间复杂度是(O(18 imes 512 imes 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)
",pos);
	if(!pos){
//		printf(" ans add(%d,%d,%d,%d)
",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
",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;
} 
原文地址:https://www.cnblogs.com/Shinomiya/p/15261220.html