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

MoyouSayuki發表於2024-04-13

[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 沒寫完。

應當集中注意力不要發呆。

相關文章