Codeforces Round 945 (Div. 2) A-D

A. Chess For Three

模拟

首先可以发现每一次对局三人的得分总和加 \(2\),所以若干次对局后得分总和也一定是 \(2\) 的倍数,然后为了使和棋数量尽可能多,一直让得分最高的两人和棋且得分数各减 \(1\) 直到无法做出和棋为止。

#include <bits/stdc++.h>

using namespace std;

#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define all(x) x.begin(), x.end()
#define ALL(x) x.begin() + 1, x.end()

using i64 = long long;
using i128 = __int128;

const int INF = 0x3f3f3f3f;

void solve() {
    vector<int> p(3);
    cin >> p[0] >> p[1] >> p[2];

    int sum = accumulate(all(p), 0);
    if (sum % 2) {
        cout << -1 << '\n';
    } else {
        int ans = 0;
        sort(all(p));
        while (p[2] != 0 && p[1] != 0) {
            p[1]--, p[2]--;
            ans++;
            sort(all(p));
        }

        cout << ans << '\n';
    }
}

void prework() {
}

int main() {
    cctie;

    prework();

    int T;
    cin >> T;

    while (T--) {
        solve();
    }

    return 0;
}

B. Cat, Fox and the Lonely Array

二分、前缀和

考虑二分 \(k\),判断数组 \(a\) 是否满足 \(k-lonely\),可通过预处理二进制下 \(n\) 个数每一位的前缀和,然后判断时对每两个相邻 \(k\) 子数组判断相等即可,判断相等看每一位的布尔值是否都相同。

证明可二分性:如果数组 \(a\)\(k-lonely\),则有 \((a_i | \ldots | a_{i+k-1}) = (a_{i+1} | \ldots | a_{i+k}) = (a_{j} | \ldots | a_{j+k-1} )= (a_{j+1} | \ldots | a_{j+k})\),而 \((a_i | \ldots | a_{i+k}) = (a_i | \ldots | a_{i+k-1}) | (a_{i+1} | \ldots | a_{i+k}),\ (a_j | \ldots | a_{j+k}) = (a_{j} | \ldots | a_{j+k-1} )|(a_{j+1} | \ldots | a_{j+k})\),则有 \((a_i | \ldots | a_{i+k})=(a_j | \ldots | a_{j+k})\),能推出 \(a\)\(k+1-lonely\)​。

时间复杂度: \(O(20nlogn)\)

#include <bits/stdc++.h>

using namespace std;

#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define all(x) x.begin(), x.end()
#define ALL(x) x.begin() + 1, x.end()

using i64 = long long;
using i128 = __int128;

const int INF = 0x3f3f3f3f;

void solve() {
    int n;
    cin >> n;

    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    vector<array<int, 20>> s(n + 1);
    for (int j = 0; j < 20; j++) {
        for (int i = 1; i <= n; i++) {
            s[i][j] = s[i - 1][j] + (a[i] >> j & 1);
        }
    } 

    auto check = [&](int k)->bool {
        for (int j = 0; j < 20; j++) {
            for (int i = k + 1; i <= n; i++) {
                if (!!(s[i][j] - s[i - k][j]) != !!(s[i - 1][j] - s[i - 1 - k][j])) {
                    return false;
                }
            }
        }
        return true;
    };

    int l = 1, r = n;
    while (l < r) {
        int mid = l + r >> 1;
        if (check(mid)) {
            r = mid;
        } else {
            l = mid + 1;
        }
    }

    cout << l << '\n';
}

void prework() {
}

int main() {
    cctie;

    prework();

    int T;
    cin >> T;

    while (T--) {
        solve();
    }

    return 0;
}

C. Cat, Fox and Double Maximum

贪心

注意到 \(n\) 位偶数,则显然答案的最大值不超过 \(\frac{n}{2}-1\),而这一值总是可以通过以下构造方式实现的。看 \(p\) 排列中 \(n\) 所处位置的奇偶性(可以避免最小值和最大值相邻的情况),若为奇数,则考虑对所有不位于两端且下标位奇数的位置作为山峰(即极大值)去构造,这些位置对应于 \(q\) 排列中的值应贪心的赋予,最小的赋予最大的,以此类推,这样最有可能在该位置构成山峰,而山峰两侧的值则是在不阻碍山峰的前提下赋予尽可能大的值,最后把未放数的位置用剩余的数补充即可;若为偶数,构造方法类似。

时间复杂度: \(O(nlogn)\)

#include <bits/stdc++.h>

using namespace std;

#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define all(x) x.begin(), x.end()
#define ALL(x) x.begin() + 1, x.end()

using i64 = long long;
using i128 = __int128;

const int INF = 0x3f3f3f3f;

void solve() {
    int n;
    cin >> n;

    vector<int> p(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> p[i];
    }

    vector<int> ord(n + 1);
    iota(ALL(ord), 1);
    sort(ALL(ord), [&](int i, int j){return p[i] < p[j];});

    set<int> S;
    for (int i = 1; i <= n; i++) {
        S.insert(i);
    }
    bool odd = (ord[n] & 1);

    vector<int> q(n + 1);
    for (int ii = 1; ii <= n; ii++) {
        int i = ord[ii];
        if (odd) {
            if ((i & 1) && i != 1) {
                q[i] = *S.rbegin();
                S.erase(--S.end());
            }
        } else {
            if (!(i & 1) && i != n) {
                q[i] = *S.rbegin();
                S.erase(--S.end());                
            }
        }
    }

    if (odd) {
        for (int i = 2; i <= n; i += 2) {
            int x = INF;
            if (i != n) {
                x = min(x, p[i + 1] + q[i + 1]);
            }
            if (i != 2) {
                x = min(x, p[i - 1] + q[i - 1]);
            }
            auto it = --S.lower_bound(x - p[i]);
            q[i] = *it;
            S.erase(it);
        }
    } else {
        for (int i = 3; i <= n; i += 2) {
            int x = INF;
            if (i != n - 1) {
                x = min(x, p[i + 1] + q[i + 1]);
            }
            if (i != 1) {
                x = min(x, p[i - 1] + q[i - 1]);
            }
            auto it = --S.lower_bound(x - p[i]);
            q[i] = *it;
            S.erase(it);
        }
    }

    for (int i = 1; i <= n; i++) {
        if (!q[i]) {
            q[i] = *S.begin();
            S.erase(S.begin());
        }
    }

    for (int i = 1; i <= n; i++) {
        cout << q[i] << " \n"[i == n];
    } 
}

void prework() {
}

int main() {
    cctie;

    prework();

    int T;
    cin >> T;

    while (T--) {
        solve();
    }

    return 0;
}

D. Cat, Fox and Maximum Array Split

交互、思维

\(l\)\(1\)\(x\)\(n^2\) 枚举到 \(n\) 去询问,当返回值为 \(n\) 时,则得出了数组中最大值 \(mx\),这样的询问最多进行了 \(n\) 次。接着我们知道,答案 \(m\) 存在的情况下,一定为 \(mx\) 的倍数,因为最大值也一定属于划分后 \(k\) 个子数组的某一个,所以我们只需要枚举 \(m\) 的值并判断是否可行即可,从 \(\frac{n}{k}*mx\) 枚举到 \(mx\),也就是枚举了 \(\frac{n}{k}\) 次,而每次判断也只需要最多 \(k\) 次询问,所以这样的询问也最多进行了 \(n\) 次,所以总次数最多进行了 \(2n\) 次。特别注意在输出答案后也要读取一个值。

时间复杂度: \(O(n)\)

#include <bits/stdc++.h>

using namespace std;

#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define all(x) x.begin(), x.end()
#define ALL(x) x.begin() + 1, x.end()

using i64 = long long;
using i128 = __int128;

const int INF = 0x3f3f3f3f;

void solve() {
    int n, k;
    cin >> n >> k;

    auto query = [&](int l, int x)->int {
        cout << "? " << l << " " << x << endl;
        int r;
        cin >> r;
        return r;
    };

    int mx = n;
    while (query(1, n * mx) != n) {
        mx--;
    }

    auto check = [&](int m)->bool {
        int cur = 0;
        for (int i = 0; i < k; i++) {
            if (cur == n) {
                return false;
            }
            int pos = query(cur + 1, m);
            if (pos == n + 1) {
                return false;
            }
            cur = pos;
        }
        return cur == n;
    };

    auto output = [&](int m)->void {
        cout << "! " << m << endl;
        int r;
        cin >> r;
    };

    for (int t = n / k; t >= 1; t--) {
        int m = t * mx;
        if (check(m)) {
            output(m);
            goto over;
        }
    }
    output(-1);
    over:
}

void prework() {
}

int main() {
    cctie;

    prework();

    int T;
    cin >> T;

    while (T--) {
        solve();
    }

    return 0;
}
posted @ 2024-05-18 16:16  会有续命晴空  阅读(457)  评论(0)    收藏  举报