P6371 [COCI 2006/2007 #6] V

题意:

使用给定的数字,组成一些在 \([A,B]\) 之间的数使得这些数每个都能被 \(X\) 整除。\(X,A,B \leq 10^{11}\)

解法:

看到这个 \(A\)\(B\),首先考虑数位 dp。但是这个 \(x\) 很大,明显无法用于状态,于是我们实行根号分治,

  • \(x \le 10^5\) 的时候,进行数位 dp,
  • \(x > 10^5\) 的时候直接暴力,大约是 \(O(1.1 \times 10^7)\),不会超时。

然后就考虑怎么数位 dp。设 \(dp_{k,sum,lim,pre}\) 表示考虑到第 \(k\) 位,这时前面的数 \(mod\ x\) 的余数是 \(sum\)\(lim,pre\) 分别表示是否取到上界以及前面是否全是 \(0\) 的数的个数。转移就非常简单,就正常数位 dp 转移即可。

Code:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10, Lim = 1e5;
ll X, A, B, dp[12][N];
int num[N];
bool c[10];
char s[20];
bool pd(ll x) {
	while(x) {
		if(!c[x % 10]) return 0;
		x /= 10; 
	}
	return 1;
}
ll dfs(int pos, int sum, bool lim, bool pre) {
	if(pos == 0) return sum == 0 && !pre;
	if(dp[pos][sum] != -1 && !pre && !lim) return dp[pos][sum];
	ll g = 0;
	int up = lim ? num[pos] : 9;
	for(int i = 0; i <= up; i++) {
		if(i == 0 && pre) g += dfs(pos - 1, 0, lim && i == up, 1);
		if(c[i] && (i != 0 || !pre)) g += dfs(pos - 1, (sum * 10 + i) % X, lim && i == up, 0);
	}
	if(!lim && !pre) dp[pos][sum] = g;
	return g;
}
ll work(ll x) {
	memset(dp, -1, sizeof dp);
	int cnt = 0;
	while(x) {
		num[++cnt] = x % 10;
		x /= 10;
	}
	return dfs(cnt, 0, 1, 1);
} 
int main() {
	scanf("%lld%lld%lld%s", &X, &A, &B, s + 1);
	for(int i = 1; i <= strlen(s + 1); i++) c[s[i] - '0'] = 1;
	if(X <= Lim) printf("%lld\n", work(B) - work(A - 1));
	else {
		ll ans = 0;
		for(ll i = X; i <= B; i += X) {
			if(i >= A && pd(i)) ans++;
		}
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2026-07-24 10:55  OIerYang  阅读(10)  评论(0)    收藏  举报