洛谷 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 进行计算即可. 策略如下:

  1. 对于位数小于当前位的, \(res += f_{i, 10, x, 0} - f_{i, 0, x, 0}\)
  2. 对于位数相同的, 计算小于最高位的 \(res += f_{num_s, p, x, 0}\)
  3. 然后对于当前不同的一位进行讨论即可, 计算出后面所需要的余数, 然后加和

\(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;
}
posted @ 2020-11-17 21:06  Aliemo  阅读(87)  评论(0编辑  收藏  举报