标签:排名 printf 状态 strlen set 一个 mod 复杂度 lan
首先我们难以计算每个在范围内的数对答案的贡献,注意到每个数的贡献组成是线性的,于是可以考虑计算每个数字对答案的贡献。
那么你会发现对于数字 \(d\),当它在所选数中排名(从大到小)为 \(i\) 时对答案的贡献就为 \(d \times 10 ^ {i - 1}\)。
那么现在的问题就转化为求数字 \(d\) 排在第 \(i\) 名的方案数。
考虑一下 \(d\) 排在第 \(i\) 名的判定条件,不难发现是比 \(d\) 大的数字填的个数小于 \(i\),大于等于 \(d\) 的数字填了不少于 \(i\) 个。
那么就有了一个暴力的 \(dp\),令 \(dp_{i, j, k, l}\) 表示当前考虑到第 \(i\) 位,当前数字 \(j\) 填了 \(k\) 个,当前大于 \(j\) 的数字填了 \(l\) 个的方案,不难发现复杂度是 \(O(700 ^ 3 \times 10 ^ 2)\) 的,还不足以通过本题。
可以发现这个状态是无法优化的,优化转移也无济于事,那么只能回到原来的判定条件。
不难发现这是由两个限制条件构成的,但第一种限制的反面实质上是第二种限制,于是可以考虑使用容斥来计算方案。
那么我们要考虑的就是计算不小于 \(d\) 的数填了 \(i\) 个的方案数,不难发现这是可以直接使用数位 \(dp\) 做到 \(O(700 ^ 2 \times 10 ^ 2)\) 的。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 700 + 5;
const int M = 10 + 5;
const int Mod = 1e9 + 7;
char s[N];
int n, ans, a[N], p[N], dp[N][M][N][2], f[M][N];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > ‘9‘ || c < ‘0‘) { if(c == ‘-‘) f = -1; c = getchar();}
while (c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar();
return x * f;
}
int Inc(int a, int b) { return (a += b) >= Mod ? a - Mod : a;}
int Dec(int a, int b) { return (a -= b) < 0 ? a + Mod : a;}
int Mul(int a, int b) { return 1ll * a * b % Mod;}
int dfs(int p, int d, int s, bool limit) {
if(s < 0) return 0;
if(p > n) return !s;
if(dp[p][d][s][limit] != -1) return dp[p][d][s][limit];
int up = (limit ? a[p] : 9), ans = 0;
rep(i, 0, up) ans = Inc(ans, dfs(p + 1, d, s - (i >= d), (limit & (i == up))));
return dp[p][d][s][limit] = ans;
}
int main() {
scanf("%s", s + 1), n = strlen(s + 1);
rep(i, 1, n) a[i] = (s[i] - ‘0‘);
memset(dp, -1, sizeof(dp));
rep(i, 0, 9) rep(j, 0, n) dfs(1, i, j, 1);
p[0] = 1;
rep(i, 1, n) p[i] = Mul(p[i - 1], 10);
rep(i, 0, 9) rep(j, 0, n) f[i][j] = Inc(max(0, dp[1][i][j][0]), max(0, dp[1][i][j][1]));
rep(i, 1, 9) f[i][0] = Dec(f[i][0], 1); f[0][n] = Dec(f[0][n], 1);
rep(i, 1, 9) rep(j, 1, n) {
int tmp = 0;
rep(k, j, n) tmp = Inc(tmp, Dec(f[i][k], f[i + 1][k]));
ans = Inc(ans, Mul(tmp, Mul(i, p[j - 1])));
}
printf("%d", ans);
return 0;
}
值得一提的是,当题目中存在两个限制时,如果一个限制的反面与另一个限制本质相同,往往可以使用满足一个条件另一个条件容斥计算来降低复杂度。
另外,如果答案要求某个和或是线性的形式,往往可以分开考虑每一部分的贡献。
CF908G New Year and Original Order
标签:排名 printf 状态 strlen set 一个 mod 复杂度 lan
原文地址:https://www.cnblogs.com/Go7338395/p/13800052.html