洛谷 P4127
题目链接: P4127 [AHOI2009]同类分布
题目大意
详见题目
solution
我感觉这题比一般的数位DP紫题要难一点, 因为维数是 \(4\) 维(我懒得压维了)
设 \(f_{i, j, k, p}\) 表示长度为 \(i\) 最高位为 \(j\) 的和为 \(k\) 对某一个数取模的余数为 \(p\) 的非萌数的个数
显然有:
\(f_{i + 1, p, j + p, (p * 10^i + k) \% x} = \sum\limits_{t = 0}^{t \leqslant 9} f_{i, t, j, k}\)
\(t\) 表示上一位填的数, \(i\) 是位数, \(p\) 是这一位填的数, \(j\) 是数位和, \(k\) 是对当前枚举的数位和取模之后的值, \(x\) 表示当前枚举的数位和
然后 solve 进行计算即可. 策略如下:
- 对于位数小于当前位的, \(res += f_{i, 10, x, 0} - f_{i, 0, x, 0}\)
- 对于位数相同的, 计算小于最高位的 \(res += f_{num_s, p, x, 0}\)
- 然后对于当前不同的一位进行讨论即可, 计算出后面所需要的余数, 然后加和
\(num_s\) 是最高位, \(x\) 是要求的位数和
答案就是所有位数和的 \(res\) 之和, 同时满足满足\([l,r]\)
参考自: asuldb
Code:
/**
* Author: Aliemo
* Data:
* Problem:
* Time: O()
*/
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
#define rr register
#define inf 1e9
#define MAXN 100010
using namespace std;
inline int read() {
int s = 0, f = 0;
char ch = getchar();
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
int l, r, mod, ans;
int num[2], base[20];
int a[20][2];
int f[20][11][172][172];
inline void spilt(int x, int s) {
num[s] = 0;
while (x) a[++num[s]][s] = x % 10, x /= 10;
}
inline void init(int x, int len) {
mod = x;
memset(f, 0, sizeof f);
for (rr int i = 0; i <= 9; i++)
f[1][i][i][i % mod]++, f[1][10][i][i % mod]++;
for (rr int i = 1; i < len; i++)
for (rr int j = 0; j <= min(x, i * 9); j++)
for (rr int k = 0; k < x; k++) {
if (!f[i][10][j][k]) continue;
for (rr int p = 0; p <= 9; p++) {
f[i + 1][p][j + p][(p * base[i] + k) % x] += f[i][10][j][k];
f[i + 1][10][j + p][(p * base[i] + k) % x] += f[i][10][j][k];
}
}
}
inline int solve(int s, int x) {
int tot = 0;
for (rr int i = 1; i < num[s]; i++) tot += f[i][10][x][0] - f[i][0][x][0];
for (rr int i = 1; i < a[num[s]][s]; i++) tot += f[num[s]][i][x][0];
int now = a[num[s]][s], cnt = now;
if (x - now < 0) return tot;
for (rr int i = num[s] - 1; i; i--) {
int t = (x - cnt * base[i] % x) % mod;
// cout << i << "\n";
for (rr int j = 0; j < a[i][s]; j++) tot += f[i][j][x - now][t];
now += a[i][s];
cnt = cnt * 10 + a[i][s];
cnt %= mod;
if (x - now < 0) break;
}
return tot;
}
signed main() {
l = read(), r = read();
spilt(l, 0), spilt(r + 1, 1);
base[0] = 1;
for (rr int i = 1; i <= 18; i++) base[i] = base[i - 1] * 10;
for (rr int i = 1; i <= num[1] * 9; i++){
init(i, num[1]);
ans += solve(1, i) - solve(0, i);
}
cout << ans;
}
时间会刺破青春表面的彩饰,会在美人的额上掘深沟浅槽;会吃掉稀世之珍!天生丽质,什么都逃不过他那横扫的镰刀。
博主写的那么好,就不打赏一下么(打赏在右边)