Loading

Codeforces Round 972 (Div. 2)

C. Lazy Narek (C)

\(dp[i][j]\) 表示到第 \(i\) 个字符串为止、以"narek"中第 \(j\) 个字母结尾时的最大得分,预处理每个字符串从第 \(j\) 个字母开始统计所得的最大长度,暴力转移即可,时间复杂度 \(O(25n)\). vp的时候由于中途花了半小时选课、没调完这题,不好评价。

const int N = 1e3 + 10, INF = 1e9;
char c[N][N], a[5] = {'n', 'a', 'r', 'e', 'k'};
int sum[N], cnt[N][5], dp[5][2];
void solve() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%s", c[i] + 1);
        sum[i] = 0;
        for(int j = 0; j < 5; j++) {
            cnt[i][j] = 0;
            int pre = (j + 4) % 5;
            for(int k = 1; k <= m; k++) {
                if(c[i][k] == a[j]) sum[i]++;
                if(c[i][k] == a[(pre + 1) % 5]) {
                    pre = (pre + 1) % 5;
                    cnt[i][j]++;
                }
            }
        }
    }
    int it = 1;
    for(int j = 1; j < 5; j++) dp[j][it] = -INF;
    dp[0][it] = 0;
    for(int i = 1; i <= n; i++) {
        it ^= 1;
        for(int j = 0; j < 5; j++) {
            dp[j][it] = dp[j][it ^ 1];
        }
        for(int j = 0; j < 5; j++) {
            for(int k = 0; k < 5; k++) {
                //printf("cnt[%d] = %d\n", k, cnt[i][k]);
                int d = j - k;
                if(d < 0) d += 5;
                if(d > cnt[i][k] || cnt[i][k] == 0) continue;
                int to = ((cnt[i][k] - d) % 5 + j) % 5;
                dp[to][it] = max(dp[to][it], dp[j][it ^ 1] + (cnt[i][k] - d) - (sum[i] - cnt[i][k] + d));
                //printf("j = %d, to = %d, ori = %d, dp = %d\n", j, to, dp[j][it ^ 1], dp[to][it]);
            }
        }
    }
    int ans = 0;
    for(int j = 0; j < 5; j++) {
        ans = max(ans, dp[j][it] - j * 2);
    }
    printf("%d\n", ans);
}

D. Alter the GCD (D)

一个妙妙小结论:对于一个序列 \(a\),其所有前缀的gcd最多只有 \(\log a_{max}\) 个。用st表维护区间gcd,暴力枚举 \(1\)\(n\) 作为左端点 \(l\),只有当 \(gcd(l, r)\)\(gcd(r + 1, n)\) 发生变化时,才有可能出现新的答案,二分查找即可。

为什么没有代码呢?因为我的代码TLE了,并且官方题解太复杂没看懂,于是被迫放弃

E1. Subtangle Game (Easy Version) (E1)

首先有一个贪心的结论:若矩阵中存在多个 \((i, j) = a\),选择最接近 \((n, m)\) 的元素、使剩余矩阵中不存在 \(a\) 是最优的。(假设远离 \((n, m)\) 的某个 \((x, y)\) 与当前 \((i, j)\) 之间存在对下一轮选择更有利的位置,对手也可以使其无法选择。)因此 \(a\) 序列中重复出现的数字、及其之后的所有数实际上是无效的,由 \(a_i\leq 7\) 可得有效数字最多只有 \(7\) 个,考虑dp,\(dp[i][j][k]\) 表示对序列中第 \(i\) 个数、在 \((j, k)\)\((n, m)\) 的子矩阵中先手操作的胜负情况。该状态可由 \(dp[i][j - 1][k],dp[i][j][k - 1]\)(必胜态)或 \(dp[i][j - 1][k - 1]\)(必败态)转移而来,根据博弈论规则计算即可。

void solve() {
    int l, n, m;
    scanf("%d%d%d", &l, &n, &m);
    for(int i = 1; i <= l; i++) {
        scanf("%d", &a[i]);
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            scanf("%d", &b[i][j]);
        }
    }
    int l1 = l;
    set <int> s;
    for(int i = 1; i <= l; i++) {
        if(s.count(a[i])) {
            l1 = i - 1;
            break;
        }
        s.insert(a[i]);
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            for(int k = 1; k <= l1; k++) {
                dp[k][i][j] = 0;
            }
        }
    }
    for(int k = l1; k; k--) {
        for(int i = n; i; i--) {
            for(int j = m; j; j--) {
                if(i < n && dp[k][i + 1][j]) dp[k][i][j] = 1;
                if(j < m && dp[k][i][j + 1]) dp[k][i][j] = 1;
                if(b[i][j] == a[k]) {
                    if(i == n || j == m || k == l1 || dp[k + 1][i + 1][j + 1] == 0) dp[k][i][j] = 1;
                }
            }
        }
    }
    if(dp[1][1][1]) printf("T\n");
    else printf("N\n");
}

E2. Subtangle Game (Hard Version) (E2)

E1的状态转移在E2显然会TLE,考虑对原dp方程进行优化,将第一维的信息转到dp值中;具体地,令 \(dp[i][j]\) 表示使 \((i, j)\)\((n, m)\) 子矩阵先手必胜的最长后缀起点。乍一看非常奇怪,但这和博弈论从终局向前推理的逻辑是一致的。对于不同的起点位置,其先后手顺序会发生变化,因此维护两个dp数组分别记录起点(在 \(a\) 序列中)为奇数/偶数的情况。若奇数情况下 \(dp[1][1] = 1\) 成立,则先手必胜,反之必败。

void solve() {
    int l, n, m;
    scanf("%d%d%d", &l, &n, &m);
    for(int i = 1; i <= l; i++) {
        scanf("%d", &a[i]);
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            scanf("%d", &b[i][j]);
        }
    }
    for(int i = 1; i <= n + 1; i++) {
        for(int j = 1; j <= m + 1; j++) {
            f[i][j] = g[i][j] = INF;
        }
    }
    fill(id + 1, id + n * m + 1, 0);
    for(int i = 1; i <= l; i++) {
        if(id[a[i]]) {
            l = i - 1;
            break;
        }
        id[a[i]] = i;
    }
    for(int i = n; i; i--) {
        for(int j = m; j; j--) {
            // 常规转移
            g[i][j] = min(g[i + 1][j], g[i][j + 1]);
            f[i][j] = min(f[i + 1][j], f[i][j + 1]);
            if(!id[b[i][j]]) continue;
            // 偶数情况,奇数必胜态在至少3步后,此时操作则奇数必败
            if((id[b[i][j]] & 1) == 0 && id[b[i][j]] + 3 <= g[i + 1][j + 1]) {
                f[i][j] = min(f[i][j], id[b[i][j]]);
            }
            // 奇数情况同理
            if((id[b[i][j]] & 1) && id[b[i][j]] + 3 <= f[i + 1][j + 1]) {
                g[i][j] = min(g[i][j], id[b[i][j]]);
            }
        }
    }
    if(g[1][1] == 1) printf("T\n");
    else printf("N\n");
}
posted @ 2024-09-24 20:36  Aderose_yr  阅读(30)  评论(0编辑  收藏  举报