W
H
X

Codeforces Round #623 (Div. 1)

Codeforces Round #623 (Div. 1)

A

B

胜者组的比赛是满二叉树的结构,按照常规方式给节点编号:\(x\) 的左儿子为 \(2x\),右儿子为 \(2x+1\)。设 \(f_{i,0/1,0/1}\) 表示以 \(i\) 为根的子树中,剩下的胜者和败者是不是关键人物。然后正常转移就好了。

C

先将所有的子串排序。预处理出 \(L_{i,j}\) 表示后缀 \([i,n]\)\([j,n]\) 的最长公共前缀,可 \(O(n^2)\) 递推。对于两个子串,若最长公共前缀未超出长度,比较下一个字符大小,否则比较长度,比较一次 \(O(1)\)

然后二分答案,计算所有划分都 \(\ge mid\)\(mid\) 为排名)的方案数是否 \(\ge k\)。用 \(f_{i,j}\) 表示前 \(i\) 个划分成 \(j\) 段且都 \(\ge mid\) 的方案数,但由于字典序是从前往后比较的,这样转移需要枚举最后一段的开头一个一个进行判断,复杂度 \(O(n^3)\)。于是可以从后往前,这样开头是定的,字典序大小有单调性,找到第一个可行的位置(满足 \(\ge mid\)),后面的都可以用,弄个后缀和就好了,\(O(n^2log\ n)\)

int n, m, L[N][N], rk[N][N];
ll k, f[N][N], s[N][N]; char a[N];
struct sub {
    int l, r;
    bool operator < (const sub &x) const {
        int p = l + L[l][x.l], q = x.l + L[l][x.l];
        return p <= r && q <= x.r ? a[p] < a[q] : r - l < x.r - x.l;
    }
} p[N * N / 2];
void qwq (ll &x) { if (x > k) x = k + 1; }
void print (int x) {
    for (int i = p[x].l; i <= p[x].r; ++i) putchar (a[i]);
    putchar ('\n');
}
int check (int x) {
    memset (s, 0, sizeof (s));
    memset (f, 0, sizeof (f));
    s[n + 1][0] = 1;
    for (int i = n, j; i >= 1; --i) {
        for (j = i; j <= n; ++j) if (rk[i][j] >= x) break;
        for (int u = 1; u <= m; ++u)
            f[i][u] += s[j + 1][u - 1], qwq (f[i][u]);
        for (int u = 1; u <= m; ++u)
            s[i][u] = s[i + 1][u] + f[i][u], qwq (s[i][u]);
        s[i][0] = 1;
    }
    return f[1][m] >= k;
}
signed main() {
    scanf ("%d %d %lld %s", &n, &m, &k, a + 1);
    for (int i = n; i >= 1; --i)
        for (int j = n; j >= 1; --j)
            if (a[i] == a[j]) L[i][j] = L[i + 1][j + 1] + 1;
    int c = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = i; j <= n; ++j) p[++c] = {i, j};
    sort (p + 1, p + c + 1);
    for (int i = 1; i <= c; ++i) rk[p[i].l][p[i].r] = i;
    int l = 1, r = c, mid, res = 0;
    while (l <= r) {
        mid = l + r >> 1;
        if (check (mid)) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    return print (res), 0;
}

D

比较麻烦的是奇环的条件。先把图随机二分染色,然后进行 \(dp\)。由于 \(k\) 比较小,所以只需重复较少次数即可得到答案(随机染色方案符合最优解)

E

\(k=1\),就是求:非严格单调上升序列 \(b\) 满足 \(\sum b_i\leq n\),有多少个 \(b\),简单 \(dp\) 即可 \(n^2\) 计算

\(k=2\),求非严格单调下降序列 \(c\) 的数量,但此时限制条件不同。若 \(c\) 存在,那么 \(a\) 的长度至少为 \(len=\sum c_i·b_i\ge\sum c_i·i\)(让大的 \(c_i\) 和小的 \(b_i\) 对应),只需满足 \(\sum c_i·i\leq n\) 即可,用类似的 \(dp\) 求解复杂度为 \(O(n^2log\ n)\)

\(k\ge 3\)\(c\) 最多只有 \(\sqrt{2n}=64\) 个数,\(k\) 越大数量越少,那么此时方案不多,直接 \(dfs+\) 剪枝:按照 \(k=2\) 的最小化法则往前倒推构造 \(k-1\) 轮,弄出最优的 \(b\),判断 \(\sum b_i \leq n\) ,若不行直接 \(return\),复杂度为......

#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
    char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 2030, mod = 998244353;
#define up(x, y) ((x += y) >= mod && (x -= mod))
int n, k, res, f[N], dp[N][N];
void work1 () {
    f[0] = 1;
    for (int i = 1; i <= n; ++i)
        for (int j = i; j <= n; ++j) up (f[j], f[j - i]);
    for (int i = 1; i <= n; ++i) up (res, f[i]);
}
void work2 () {
    dp[0][0] = 1;
    for (int i = n; i >= 1; --i)
        for (int j = 1; j * i <= n; ++j)
            for (int k = i * j; k <= n; ++k)
                up (dp[j][k], dp[j - 1][k - i * j]);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j) up (res, dp[i][j]);
}
int a[N], b[N], c[N], m, tp;
int check () {
    int m = 0, s = 0;
    for (int i = 1; i <= tp; ++i) b[++m] = a[i];
    for (int t = 1; t < k; ++t) {
        s = 0; int mm = 0;
        for (int i = 1; i <= m; ++i) s += b[i];
        if (s > n) return 0;
        for (int i = 1; i <= m; ++i)
            for (int j = 1; j <= b[m - i + 1]; ++j) c[++mm] = i;
        for (int i = 1; i <= mm; ++i) b[i] = c[i]; m = mm;
    }
    s = 0; for (int i = 1; i <= m; ++i) s += b[i];
    if (s > n) return 0; else return ++res, 1;
}
int work3 (int now, int s) {
    if (s && !check ()) return 0;
    for (int i = now; i + s <= n; ++i) {
        a[++tp] = i;
        if (!work3 (i, s + i)) return --tp, 1; --tp;
    }
    return 1;
}
signed main() {
    read (n), read (k);
    if (k == 1) work1 ();
    else if (k == 2) work2 ();
    else work3 (1, 0);
    return printf ("%d\n", res), 0;
}

F

题意太长太毒瘤...

posted @ 2021-06-08 09:14  -敲键盘的猫-  阅读(33)  评论(0编辑  收藏  举报