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 是否满足 klonely,可通过预处理二进制下 n 个数每一位的前缀和,然后判断时对每两个相邻 k 子数组判断相等即可,判断相等看每一位的布尔值是否都相同。

证明可二分性:如果数组 aklonely,则有 (ai||ai+k1)=(ai+1||ai+k)=(aj||aj+k1)=(aj+1||aj+k),而 (ai||ai+k)=(ai||ai+k1)|(ai+1||ai+k), (aj||aj+k)=(aj||aj+k1)|(aj+1||aj+k),则有 (ai||ai+k)=(aj||aj+k),能推出 ak+1lonely​。

时间复杂度: 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 位偶数,则显然答案的最大值不超过 n21,而这一值总是可以通过以下构造方式实现的。看 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

交互、思维

l1xn2 枚举到 n 去询问,当返回值为 n 时,则得出了数组中最大值 mx,这样的询问最多进行了 n 次。接着我们知道,答案 m 存在的情况下,一定为 mx 的倍数,因为最大值也一定属于划分后 k 个子数组的某一个,所以我们只需要枚举 m 的值并判断是否可行即可,从 nkmx 枚举到 mx,也就是枚举了 nk 次,而每次判断也只需要最多 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 @   会有续命晴空  阅读(434)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示