[CF1942] CodeTON Round 8 (Div. 1 + Div. 2, Rated, Prizes! A~E 题解

[CF1942] CodeTON Round 8 (Div. 1 + Div. 2, Rated, Prizes! A~E 题解

A. Farmer John's Challenge

只有两种情况,一种是单调递增,这时 \(k = 1\),另一种是所有数都相同,这时 \(k = n\)

B. Bessie and MEX

首位可以确定,然后从前往后增量构造 \(p\) 即可。

void work() {
    cin >> n;
    s.clear();
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 0; i < n; i ++)
        s.insert(i), st[i] = 0;
    if(a[1] == 1) p[1] = 0;
    else p[1] = -a[1];
    s.erase(p[1]), st[p[1]] = 1;
    for(int i = 2; i <= n; i ++) {
        int mex = *s.begin();
        if(mex - a[i] >= 0 && mex - a[i] < n && !st[mex - a[i]]) {
            p[i] = mex - a[i];
        }
        else {
            p[i] = mex;
        }
        st[p[i]] = 1, s.erase(p[i]);
    }
    for(int i = 1; i <= n; i ++)
        cout << p[i] << ' '; cout << '\n';
    
    return ;
}

C1. Bessie's Birthday Cake (Easy Version)

注意到相邻连续的一段特殊点可以被缩掉,可以这样连边:

image-20240413113034086

然后这一段点就只剩下左右端点了,最后会得到若干个特殊点,这样从一个点向其它所有点连边,然后把边上的边连起来,这样就是最优解了。

C2. Bessie's Birthday Cake (Hard Version)

考虑在哪加入特殊点会带来贡献。

只有最后连边的时候,相邻特殊点中间可以加入新贡献。

每次加入一个点会有两种贡献,分别是 2/3。

对于 3 的贡献可以按凸包大小排序,然后对于 2 的直接放到最后。

int n, x[N], m, y, d[N], idx;

void work() {
    cin >> n >> m >> y;
    idx = 0;
    for(int i = 1; i <= m; i ++)
        cin >> x[i];
    sort(x + 1, x + m + 1);
    int ans = 0, tot = m, cnt = 1;
    for(int i = 2; i <= m; i ++) {
        if(x[i] == x[i - 1] + 1) {
            tot --;
            cnt ++;
        }
        else {
            tot ++;
            ans += cnt - 2;
            cnt = 1;
        }
        if(x[i] == x[i - 1] + 2) ans ++;
        if(x[i] - x[i - 1] > 2) d[++ idx] = x[i] - x[i - 1] - 1;
    }
    if(x[1] + n - x[m] > 2) d[++ idx] = x[1] + n - x[m] - 1;
    if(x[1] + n - x[m] == 1) {
        cnt ++;
        ans += cnt - 2;
    }
    else tot ++, ans += cnt - 2;
    if(x[1] + n - x[m] == 2) ans ++;
    sort(d + 1, d + idx + 1, [](int a, int b) {return ((a & 1) == (b & 1)) ? (a < b) : ((a & 1) > (b & 1));});
    for(int i = 1; i <= idx; i ++) {
        if(y >= d[i] / 2) ans += d[i], y -= d[i] / 2;
        else {
            ans += y * 2;
            break;
        }
    }
    cout << ans + tot - 2 << '\n';
    
    return ;
}

D. Learning to Paint

考虑 DP,设 \(f_{i, j}\) 表示前 \(i\) 个位置第 \(j\) 大的答案是多少。

转移来自所有 \(p < i\) 位置的前 \(k\) 大,转移总数很多但是我们只要前 \(k\) 大,所以用堆维护即可。

int n, k, a[N][N], f[N][N], g[N];
priority_queue<PII> heap;

void work() {
    cin >> n >> k;
    for(int i = 1; i <= n; i ++) {
        for(int j = i; j <= n; j ++)
            cin >> a[i][j];
    }
    for(int i = 0; i <= n; i ++)
        for(int j = 0; j <= k; j ++) f[i][j] = -INF;
    f[0][1] = 0;
    for(int i = 1; i <= n; i ++) {
        while(heap.size()) heap.pop();
        for(int j = 1; j <= i - 1; j ++) {
            g[j] = 1;
            heap.push({f[j - 1][1] + a[j + 1][i], j});
        }
        heap.push({a[1][i], 0});
        for(int j = 1; j <= k; j ++) heap.push({f[i - 1][j], -1});
        for(int j = 1; j <= k; j ++) {
            if(heap.empty()) break;
            auto [x, y] = heap.top(); heap.pop();
            f[i][j] = x;
            if(y > 0 && g[y] < k) heap.push({f[y - 1][++ g[y]] + a[y + 1][i], y});
        }
    }
    for(int i = 1; i <= k; i ++) cout << f[n][i] << ' '; cout << '\n';
    
    return ;
}

E. Farm Game

A 的牛只有向右走的时候才会给局面带来改变,因为二者轮流动,显然相邻是必败态,以此类推可以知道后者获胜当且仅当所有样式 AB 之间的距离都是偶数。

考虑计数这个东西,因为对称,所以只需要计数 A 在左边的情况。可以枚举所有 AB 的间隔和,然后用除以 2 用挡板法算出来一种间隔和的贡献,因为还要放置 AB,这部分的方案数是:

\[\binom{l - i - n}{n} \]

可以看作把 \(l\) 里面去掉 \(i\) 段,这样需要组合 \(n\) 相邻点,减掉 \(n\) 段就是组合数了。

// LUOGU_RID: 155511987
// Problem: Farm Game
// Author: Moyou
// Copyright (c) 2024 Moyou All rights reserved.
// Date: 2024-04-12 19:11:35

#include <iostream>
using namespace std;
const int N = 2e6 + 10, mod = 998244353, M = N;

int n, l;
int qmi(int a, int b) {
	int res = 1;
	while(b) {
		if(b & 1) res = 1ll * res * a % mod;
		b >>= 1; a = 1ll * a * a % mod; 
	}
	return res;
}
int fac[M], ifac[M];
void pre() {
    fac[0] = ifac[0] = 1;
    for(int i = 1; i <= M - 10; i ++) fac[i] = 1ll * fac[i - 1] * i % mod;
    ifac[M - 10] = qmi(fac[M - 10], mod - 2);
    for(int i = M - 11; i; i --) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
}
int C(int n, int m) {
    if(n < m) return 0;
    return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}

void work() {
    cin >> l >> n;
    int ans = 0;
    for(int i = 0; i <= l - n * 2; i += 2)
        ans = (ans + 1ll * C(l - i - n, n) * C(i / 2 + n - 1, n - 1) % mod) % mod;
    cout << (2ll * (C(l, n * 2) - ans) % mod + mod) % mod << '\n';
    return ;
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    pre();
    int T = 1; 
    cin >> T;
    while (T--) work();

    return 0;
}

总结

C1, C2切慢了,后面 D 没写完。

应当集中注意力不要发呆。

posted @ 2024-04-13 11:49  MoyouSayuki  阅读(33)  评论(0编辑  收藏  举报
:name :name