Solution Set 2

发布时间 2023-10-25 00:10:50作者: wuyoudexian

集合之和

Attachments - 2022 CCPC Henan Provincial Collegiate Programming Contest - Codeforces

题意

构造一个集合,使得集合中每两个数相加,得到的数再组成的一个集合,使得新集合的大小为\(n\)

思路

\(n\)为奇数时,构造集合{1,2,……(n + 1) / 2},得到的新集合中的数为2~n+1。如果\(n\)为偶数,把第一个数改为0即可。注意当\(n\le4\)\(n\)为偶时是无法构造的。

代码

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    if(n & 1) {
        cout << (n + 1) / 2 << "\n";
        for(int i = 1; i <= (n + 1) / 2; i++) {
            cout << i << " ";
        }
    } else {
        if(n <= 4) {
            cout << -1 << "\n";
        } else {
            cout << n / 2 << "\n" << 0 << " ";
            for(int i = 2; i <= n / 2; i++) {
                cout << i << " ";
            }
        }
    }

    return 0;
}

龙语魔法

Attachments - The 7-th BIT Campus Programming Contest for Junior Grade Group - Codeforces

题意

给定一个序列,找到所有子区间中区间和第\(k\)大的区间,输出其区间和。

思路

二分答案+双指针

代码

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    i64 n, k;
    cin >> n >> k;

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

    i64 lo = 1, hi = 1e18;
    while(lo < hi) {
        i64 mid = lo + hi >> 1;
        i64 i = 0, j = 0, sum = 0, cnt = 0;
        while(j < n) {
            sum += a[j++];
            while(j < n && sum <= mid) {
                sum += a[j++];
            }
            cnt += n - j + 1;
            
            while(sum - a[i] > mid) {
                sum -= a[i++];
                cnt += n - j + 1;
            }
            sum -= a[i++];
        }

        if(n * (n + 1) / 2 >= k + cnt) {
            hi = mid;
        } else {
            lo = mid + 1;
        }
    }

    cout << hi << "\n";

    return 0;
}

Xenia and Bit Operations

Problem - D - Codeforces

题意

\(2^n\)个数和\(m\)个操作,每次修改一个数,然后你要输出 ( (a1|a2)xor(a3|a4) )|( (a5|a6)xor(a7|a8) )....

即 or 与 xor 交替计算。

思路

建一棵二叉树,每次修改只要\(\log\)的时间,根据二叉树的层数的奇偶确定是xor操作还是or操作。

代码

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;

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

    for(int i = (1 << n) - 1; i > 0; i--) {
        if((n - __lg(i)) % 2) {
            a[i] = a[i * 2] | a[i * 2 + 1];
        } else {
            a[i] = a[i * 2] ^ a[i * 2 + 1];
        }
    }

    for(int i = 0; i < m; i++) {
        int p, b;
        cin >> p >> b;
        p += 1 << n;
        a[--p] = b;
        while(p /= 2) {
            if((n - __lg(p)) % 2) {
                a[p] = a[p * 2] | a[p * 2 + 1];
            } else {
                a[p] = a[p * 2] ^ a[p * 2 + 1];
            }
        }
        cout << a[1] << "\n";
    }

    return 0;
}

Good Subarrays

Problem - C - Codeforces

题意

定义区间和等于区间长度的区间为好区间,求给出序列的好区间的数量。

思路

\(f_i\)\(i\)的前缀和,则好区间满足\(f_r-f_l=r-l\),变化一下可得\(f_r-r=f_l-l\)。用map记录每个右端点前面满足条件的左端点数。

代码

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

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

    vector<int> pre(n + 1);
    for(int i = 1; i <= n; i++) {
        pre[i] = pre[i - 1] + (s[i - 1] - '0');
    }

    map<int, int> cnt;
    cnt[0] = 1;
    i64 ans = 0;
    for(int i = 1; i <= n; i++) {
        ans += cnt[pre[i] - i]++;
    }

    cout << ans << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;

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

    return 0;
}

Number of Ways

Problem - C - Codeforces

题意

给定一个序列,求有多少种方式把该序列分为三个部分,使得三个部分的和相等。

思路

遍历前缀和,如果有前缀和等于序列总和的1/3,则该点可作为一个分割第一二部分的点。如果有点的前缀和等于总和的2/3,那么该点可作为分割第二三部分的点。

代码

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;

    vector<i64> pre(n + 1);
    i64 sum = 0;
    for(int i = 1, x; i <= n; i++) {
        cin >> x;
        sum += x;
        pre[i] = pre[i - 1] + x;
    }

    i64 ans = 0, cnt = 0;
    for(int i = 1; i <= n; i++) {
        if(i >= 2 && i <= n - 1 && pre[i] * 3 == sum * 2) {
            ans += cnt;
        }
        if(i <= n - 2 && pre[i] * 3 == sum) {
            cnt += 1;
        }
    }

    cout << ans << "\n";

    return 0;
}