Codeforces Round 941 (Div. 2) A-D

A. Card Exchange

贪心

如果有某个数出现 \(k\) 次及以上,则通过操作使其数量变为 \(k\),再变为其他出现过的数,则会增加至至少 \(k\) 个,一直进行如上操作,可以发现数组最终只剩 \(k - 1\) 个数;否则为 \(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;

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

    vector<int> cnt(101);
    for (int i = 1; i <= n; i++) {
        cnt[c[i]]++;
    }

    bool f = false;
    for (int i = 1; i <= 100; i++) {
        if (cnt[i] >= k) {
            f = true;
        }
    }

    if (f) {
        cout << k - 1 << '\n';
    } else {
        cout << n << '\n';
    }
}

void prework() {

}

int main() {
    cctie;

    prework();

    int T;
    cin >> T;

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

    return 0;
}

B. Rectangle Filling

思维

对于网格左上角的方格而言,当且仅当网格最右侧和最下侧都出现了与左上角相同的颜色时可以实现全网格同色,右下角同理,左下和右上已经包括在前两种情况中了,不用重复判断。

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

#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, m;
    cin >> n >> m;

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

    bool col = false, row = false;
    for (int i = 1; i <= n; i++) {
        if (a[i][m] == a[1][1]) {
            col = true;
        }
    }
    for (int i = 1; i <= m; i++) {
        if (a[n][i] == a[1][1]) {
            row = true;
        }
    }

    if (col && row) {
        cout << "YES\n";
    } else {
        col = false, row = false;
        for (int i = 1; i <= n; i++) {
            if (a[i][1] == a[n][m]) {
                col = true;
            }
        }
        for (int i = 1; i <= m; i++) {
            if (a[1][i] == a[n][m]) {
                row = true;
            }
        }
        if (col && row) {
            cout << "YES\n";
        } else {
            cout << "NO\n";
        }
    }
}

void prework() {

}

int main() {
    cctie;

    prework();

    int T;
    cin >> T;

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

    return 0;
}

C. Everything Nim

博弈论

考虑对原数组排序去重后考虑,若最小石堆石头数大于 \(1\),则先手必胜,考虑几个石堆中的石头数连续时,先手可以决定拿 \(1\)\(n - 1\) 来让任意一方最后拿完这些石堆,因此爱丽丝只要一直最优选择,一定获胜;若最小石堆石头数为 \(1\),则与该石堆连续的所有石堆的操作都是固定的,即双方轮流取 \(1\),当这些石堆取完之后,可以发现回到了最小石堆石头数大于 \(1\) 的情况,于是只要判断此时谁是先后手即可,注意特判已经无非空石堆的情况。

时间复杂度:\(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> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    sort(ALL(a));
    a.erase(unique(ALL(a)), a.end());
    n = a.size() - 1;

    int t = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i] - a[i - 1] != 1) {
            t = i;
            break;
        }
    }
    if ((t & 1) || (t == 0 && (n & 1))) {
        cout << "Alice\n";
    } else {
        cout << "Bob\n";
    }
}

void prework() {

}

int main() {
    cctie;

    prework();

    int T;
    cin >> T;

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

    return 0;
}

D. Missing Subsequence Sum

构造、位运算

​ 先考虑如何构造子序列和包含 \([1,n]\) 的数组,显然数组 \(2^0,2^1,2^2,...,2^{19}\) 满足条件,然后考虑如何仅仅把 \(k\) 去除,记 \(k\) 的最高位为 \(h\),则将 \(2^h\) 替换为 \(k-1-\sum_{i=0}^{h-1}2^i\),则前 \(h+1\) 项显然满足子序列和包含 \([1,k-1]\),此时问题转变为考虑如何更改第 \(h+2\) 项及往后的数或添数来使子序列和包含 \([k+1,n]\),这里考虑通过添数来实现。

​ 可以发现现在无法构造出来的区间为 \([k, 2^{h+1}-1]\) 以及在两端点加上若干不重复的高位一(\(2^t(t>h)\))的所有区间并,于是添 \(k+1\) 至数组,这时包含了区间 \([k+1,2k]\) (由 \([1,k-1]\) 和数 \(k+1\) 得到),由前面的定义可知 \(2k>2^{h+1}-1\),则此时无法构造出来的区间转变为 \([k, k]\) 以及在两端点加上若干不重复的高位一(\(2^t(t>h)\))的所有区间并,而 \(k\) 本身无需构造,所以只需考虑由 \(+k\) 延申出来的数即可。

​ 此处考虑添 \(\sum_{i=0}^{h}2^i\)\(2^{h+1}-1\) 至数组,而该数与已添加的数 \(k + 1\) 恰能凑出 \(k + 2^{h+1}\),而这最小项凑出后,通过进位,之后的每一个类似形式的值也都能构造出来。

最后注意特判一下 \(k=1\) 的情况,该方法所构造的数组的有效数的个数最多为 \(23\) 个。

#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;

    int x = 1, s = 0;
    vector<int> a(26);
    int i;
    for (i = 1; i <= 25; i++) {
        if (s + x < k) {
            a[i] = x;
            s += x;
            x <<= 1;
        } else {
            a[i] = k - 1 - s;
            break;
        }
    }
    a[++i] = k + 1;
    if ((s + (x << 1)) != k) {
        a[++i] = (s + (x << 1));
    }
    if (k == 1) {
        a[++i] = 3;
    }
    while (i < 25) {
        x <<= 1;
        a[++i] = x;
    }

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

void prework() {

}

int main() {
    cctie;

    prework();

    int T;
    cin >> T;

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

    return 0;
}
posted @ 2024-04-28 21:01  会有续命晴空  阅读(108)  评论(0编辑  收藏  举报