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;
}

浙公网安备 33010602011771号