码迷,mamicode.com
首页 > 其他好文 > 详细

CF908G New Year and Original Order

时间:2020-10-12 19:59:07      阅读:22      评论:0      收藏:0      [点我收藏+]

标签:排名   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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!