Codeforces Round 981 (Div. 3) 题解

A

人类检测器。

手推不难发现,奇数都在负半轴,偶数都在正半轴,然后判断奇偶就行了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long

void solve () {
    int n; cin >> n;
    if (n & 1) cout << "Kosuke\n";
    else cout << "Sakurako\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

B

纯模拟题,直接模拟正方形的每一个对角线,在每一条对角线上取绝对值最大的负数即可。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long

void solve () {
    int n; cin >> n;
    vector <vector <int> > a(n, vector<int>(n));
    for (int i = 0;i < n;i++)
        for (int j = 0;j < n;j++) 
            cin >> a[i][j];
    ll ans = 0;
    for (int i = 0;i < n;i++) {
        int x = i, y = 0;
        int tmp = 0;
        while (x < n && y < n) {
            if (a[x][y] < 0) tmp = max(tmp, abs(a[x][y]));
            x++, y++;
        }
        ans += tmp;
    }
    for (int j = 1;j < n;j++) {
        int x = 0, y = j, tmp = 0;
        // cout << x << " " << n << "\n";
        while (x < n && y < n) {
            if (a[x][y] < 0) tmp = max(tmp, abs(a[x][y]));
            // cout << "[+] " << x << " " << y << "\n";
            x++, y++;
        }
        ans += tmp;
    }
    cout << ans << "\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

C

被自己的脑子小坑了一把,这里可以直接先得到交换前的答案和交换后的答案,然后比较,看看哪个更优进行操作就行。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long

void solve () {
    int n; cin >> n;
    vector <int> a(n+1);
    for (int i = 1;i <= n;i++) cin >> a[i];
    ll ans = 0;
    int mid = (n+1)/2;
    for (int i = 2;i <= mid;i++) {
        int pre = (a[i] == a[i-1]) + (a[n-i+1] == a[n-i+2]);
        int nex = (a[i] == a[n-i+2]) + (a[i-1] == a[n-i+1]);
        if (nex < pre) swap(a[i], a[n-i+1]);
    } 
    for (int i = 1;i < n;i++) if (a[i] == a[i+1]) ans++;
    cout << ans << "\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

D

个人觉得这个比 C 简单。

这个可以直接贪心,利用前缀和统计,然后对于每一种前缀和记录它最近上一次的出现位置,然后判断这个位置会不会和目前最右边的区间重叠,随后统计答案就行了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const int N = 1e5+100;

// int a[N], sum[N];

void solve () {
    int n; cin >> n;
    vector <ll> a(n+2), sum(n+2);
    map <ll, ll> pos;
    for (int i = 1;i <= n;i++) {
        cin >> a[i];
        if (i == 1) sum[i] = a[i];
        else sum[i] = sum[i-1] + a[i];
        pos[sum[i]]= -1;
    }
    pos[0] = 0;
    int maxr = -1;
    ll ans = 0;
    // for (int i = 1;i <= n;i++) cout << sum[i] << " ";
    // cout << "n";
    for (int i = 1;i <= n;i++) {
        if (pos[sum[i]] == -1) pos[sum[i]] = i;
        else {
            int l = pos[sum[i]];
            // cout << "[+] " << sum[i] << "===> " << l << "----" 
                // << maxr << 'n';
            if (l >= maxr) ans++, maxr = i;
            pos[sum[i]] = i;
        }
    }
    cout << ans << "\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

E

绷,这题考并查集的考法的确新颖。

我们思考一下题目给出的两个条件:

\[p_i = i \\ p_{p_i} = i \]

具体点来说:

  • 位置上是自己。

  • 位置上的数字和另一个位置上的数字对换了。

如果用图的思维来想,那么一个是自环一个就是两个节点组成的循环。

同时我们知道这是一个排列,试着将位置上的值和位置的编号建立一个有向边,发现最后是一定可以变成若干个环。而已经说过了合法的情况只有上面两种,基于贪心,我们选择第二种,不难发现,我们设每个连通块的大小为 \(size_i\),那么我们只需要变更 \(\lfloor \frac{size_i-1}{2}\rfloor\) 次,因为最后一队会在最后自己合法。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long

void solve () {
    int n; cin >> n;
    vector <int> p(n+1), fa(n+1), si(n+1);
    for (int i = 1;i <= n;i++) si[i] = 1, fa[i] = i;
    auto find = [&](auto self, int a) -> int {
        if (fa[a] == a) return a;
        return fa[a] = self(self, fa[a]);
    };
    auto merge = [&](int a, int b) -> void {
        int faa = find(find, fa[a]), fab = find(find, fa[b]);
        if (faa != fab) {
            // fa[faa] = fab;
            if (si[faa] < si[fab]) swap(si[faa], si[fab]);
            si[faa] += si[fab];
            fa[fab] = faa;
        }
    };
    for (int i = 1;i <= n;i++) {
        cin >> p[i];
        merge(p[i], i);
    }

    ll ans = 0;
    for (int i = 1;i <= n;i++) 
        if (fa[i] == i) ans += (si[i]-1)/2;
    cout << ans << "\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

F

逆天数论题,讨论的是斐波那契数列的模数周期性,其实就是皮亚诺定理,但是我真的不会QWQ,也是请教别人得到的。

给出几个连接大家学习学习:皮亚诺定理

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const int N = 1e6+100;
const int MOD = 1e9+7;

int f[N];

void solve () {
    ll n, k; cin >> n >> k;
    f[1] = f[2] = 1;
    ll p = 0; int i = 3;
    while (1) {
        f[i] = (f[i-1] + f[i-2]) % k;
        if (!f[i]) { p = i; break; }
        i++;
    }
    if (k == 1) p = 1;
    cout << (n % MOD) * (p % MOD) % MOD << "\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}