0%

Codeforces - Round 832 Div 2

Practice.

A. Two Groups

题意

给定一个数组 ,可以为负数,从数组 中取出某些数作为序列 ,剩余作为序列 ,输出 的最大值。序列可以为空。

思路

考虑到 ,我们可以发现原数组的总和的绝对值即为最大值。

时间复杂度:

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 2e5+10;

signed main(){
    ios::sync_with_stdio(false), cin.tie(NULL), cout.tie(NULL);
    int t;
    cin >> t;
    while(t --){
        int n;
        cin >> n;
        int ans = 0;
        for(int i=1;i<=n;i++) {
            int cur;
            cin >> cur;
            ans += cur;
        }
        cout << abs(ans) << '\n';
    }
}

暴力枚举碰运气过了可还行

B. BAN BAN

题意

给定一个整数 ,对于由 组成的字符串,输出至少交换几次,使所有长度为 可不连续子序列不包含

思路

显然,我们希望 能移到后面, 能移到前面,那么我们不妨将第 和第 交换。

当然,若 为奇数,中间的那个字符串直接反转即可。

时间复杂度:

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 2e5+10;

signed main(){
    ios::sync_with_stdio(false), cin.tie(NULL), cout.tie(NULL);
    int t;
    cin >> t;
    while(t --){
        int n;
        cin >> n;
        cout << n / 2 + n % 2 << '\n';
        for(int i=0;i<n/2;i++) cout << 1 + i * 3 << ' ' << 3 * n - (i * 3) << '\n';
        if(n % 2 == 1) cout << 3 * (n / 2) + 1 << ' ' << 3 * (n / 2) + 3 << '\n';
    }
}

纯纯找规律((

C. Swap Game

题意

给定一个序列,规定每个玩家可以选择一个数,将其与第一个数交换,并将第一个数扣去 。定义最后将 交换到第一个位置上的玩家输,在 先手的条件下,输出最后的赢家。

思路

首先,若最小的数是第一个数,那么先手就会将最小的数放到后面,从而使后手具有必胜策略:我只需每次都挑大的数,那么对方就只能挑小的数,从而让对方最后只能取 。反之同理。

时间复杂度:

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 2e5 + 10, inf = 0x3f3f3f3f;

signed main(){
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin >> t;
    while(t --){
        int n;
        cin >> n;
        int minn = inf, a0;
        cin >> a0;
        minn = min(minn, a0);
        for(int i=1;i<n;i++){
            int cur;
            cin >> cur;
            minn = min(minn, cur);
        }
        cout << (minn == a0 ? "Bob" : "Alice") << '\n';
    }
}

当然和总和的奇偶性没关系

D. Yet Another Problem

题意

给定一个序列 ,定义一次操作为选定任意长度为奇数的区间,将区间内的数全都改为整个区间的异或值。对于每个区间询问,输出最小操作数,使区间的数都变为 。无解输出

思路

首先,若没有区间奇偶性的限制,我们直接预处理出前缀异或,然后用类似于前缀和的方式计算即可。

当然,也可以用一下前缀和,毕竟我们需要知道这个区间是否已经全为

若查询的区间长度为偶数,那么我们只能挑选两个长度为奇数的区间。考虑到对一个子区间操作后,剩余的区间的异或值一定为 ,所以我们只需枚举所有长度为奇数的右端的子区间即可(左端也行)。

不过,我们不可以枚举,因为数据量实在太大了。所以,我们需要进行预处理。对于一个右端点,若它为偶数,那么以它为右端点的区间的左端点一定是奇数,从而我们可以进行递推,找到两个异或值相等且奇偶性不同的点即可。

时间复杂度:

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 2e5 + 10;

int a[N], xo[N], sum[N], pre[N];

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int n, q;
    cin >> n >> q;
    vector<map<int, int>> mp(2);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        xo[i] = xo[i - 1] ^ a[i];
        sum[i] = sum[i - 1] + a[i];
        if(mp[1 - i % 2].count(xo[i])) pre[i] = mp[1 - i % 2][xo[i]];
        mp[i % 2][xo[i]] = i;
    }
    while (q--) {
        int l, r;
        cin >> l >> r;
        if ((xo[r] ^ xo[l - 1]) != 0) {
            cout << -1 << '\n';
            continue;
        }
        if (sum[r] - sum[l - 1] == 0) {
            cout << 0 << '\n';
        } else if ((r - l + 1) % 2 == 1) {
            cout << 1 << '\n';
        } else {
            if (a[r] == 0 || a[l] == 0) {
                cout << 1 << '\n';
                continue;
            }
            cout << (pre[r] >= l ? 2 : -1) << '\n';
        }
    }
}

二分半天不过…