Codeforces Round #882 (Div. 2) ABCDF

目录

A. The Man who became a God

题意:

思路:

代码:

B. Hamon Odyssey

题意:

思路:

代码:

C. Vampiric Powers, anyone?

题意:

思路:

代码:

D. Professor Higashikata

题意:

思路:

代码:

F. The Boss's Identity


A. The Man who became a God

题意:

我说的过于繁琐,故删除

思路:

观察发现,我们分成k部分,相当于插入了k - 1个隔板,并且我们的总和只减少了那些被选择成为隔板的数的f,因此我们选择最大的k - 1个f,从总和中删除(或者和我代码里一样不计算)即可

然后我这里

需要注意:i = 1, f = 0,因为实际上,按照题中的要求只会产生从2到n这些个f,因为我从1开始的,然而1本身并不会产生f,故需要设置为0(后面计算也懒得改下标从2开始了QAQ,毕竟+0和没加是一样的)

代码:

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

void solve() {
    int n, k;
    cin >> n >> k;

    vector<int> a(n + 1, 0), f(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        f[i] = abs(a[i] - a[i - 1]);
    }

    f[1] = 0;
    sort(f.begin() + 1, f.begin() + 1 + n);
    
    i64 ans = 0;
    for (int i = 1; i <= n - k + 1; i++) {
        ans += f[i];
    }
    cout << ans << "\n";
}

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

    int t;
    cin >> t;

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

    return 0;
}

B. Hamon Odyssey

题意:

首先我们定义,对于任意一组,其计算结果为组内各个数字依次与得到的

n个数,问你在保证各组计算后的结果相加的结果最小的同时最多能够分成多少组

思路:

1、显然,对于一组,若其组内计算的结果为0(组内各个数相与的结果为0),那么该组可以成立。

2、并且如果遍历了所有数,仍未遇到一组满足计算结果为0的,那至少需要一组

因此,我们贪心遍历一遍,遇到能成立的,ans ++即可,并且注意处理2中的情况即可

代码:

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

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

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

    i64 sum = a[0], ans = 0;
    for (int i = 0; i < n; i++) {
        sum &= a[i];
        if (sum == 0) {
            ans ++;
            sum = a[i + 1];
        }
    }
    cout << max(1LL, ans) << "\n";
}

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

    int t;
    cin >> t;

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

    return 0;
}

C. Vampiric Powers, anyone?

题意:

每次可以取一个后缀异或和放在数组最后,可以进行无限次操作,问你经过操作后数组中的最大值为多少

思路:

关键点在于将此问题转化为最大子段异或和

1、首先明确,我们能通过下面图示(有点丑)操作,实现将任意一段连续区间的异或和放入数组的结尾

2、有了1的说明,我们能加入到数组的,一定就是某个连续子段的异或和,我们最大化这个连续子段异或和即可

注意:本题数据范围ai < 2^8, 因此直接可以map存出现过的前缀异或和,如果数据更大需要Trie树来做

代码:

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

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

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

    vector<int> hav((1 << 8 + 1), 0);
    /*
        首先明确本题所求为最大异或子段和
        cur_xor : 记录从1到i的前缀异或和
        hav : 记录出现过的、从第1位开始的前缀异或和(比如1~3, 1~4 ... 1~n等,因为题目告诉ai<2^8所以可以直接这么做,否则需要用trie树)
        pre : 从0 ~ 2^8 枚举可能的前缀异或和
    */
    hav[0] = 1;
    int cur_xor = 0, ans = 0;
    for (int i = 0; i < n; i++) {
        cur_xor ^= a[i];
        hav[cur_xor] = 1; 
        /*
            划重点 : 下面这步,本质上就是在枚举所有可能的区间异或和
            举个例子 : 当前cur_xor = 10, 之前出现过的pre有很多个(a b c d ... 等)
            但无法直接确定排除掉哪个pre(前缀异或和)会让cur_xor更大,所以我直接枚举所有进行检查cur_xor ^ pre是否会变大即可
            这里需要自己动手写一写 体会一下会更清晰OvO
        */
        for (int pre = 0; pre < (1 << 8); pre++) {
            if (hav[pre]) { 
                ans = max(ans, pre ^ cur_xor);
            }
        }
    }
    cout << ans << "\n";
}

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

    int t;
    cin >> t;

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

    return 0;
}

D. Professor Higashikata

题意:

每次操作可以对s串中的任意两个字符进行交换,从s串中截取出一堆字串合并成为t串(即给你的那些l,r),对于每个询问,会将他给定那个位置的字符反转,问你最少多少次交换操作可以让拼接得到的t的字典序最大。并且操作存在后效性。

思路:

因为本人刚学树状数组,所以题解可能存在一些误差,仅供参考

首先明确尽可能地把1放前面,然后有以下两种情况:

我们令字符串s中的1的个数为cnt1, t串的长度为siz

1:cnt1 >= siz,那么显然我们需要交换siz - siz中已经有的1的个数,次即可

2:cnt1 < siz 我们需要交换的次数为t串的前cnt1个字符中,不为1的个数之和次即可

根据上面信息,我们发现需要做几件事情:

1、保证每个串的优先级为其读入顺序,因为我们一定是从t串开头进行逐一处理才能保证前面的1是最多的,而t串的顺序即读入这些串的顺序组合

2、读入串的时候我们需要统计其中1的个数,并且考虑可能多次读入相同的 or 重合面积大的串;前者我们考虑用树状数组去维护每个区间内1的个数,后者我们考虑用并查集处理去合并区间

3、因为每次会动态更新区间某个点的01情况,因此同样使用树状数组维护(区间单点修改)

个人感觉这个题是有一些离散化的思想在里面:

把那些不连续的串,按给定顺序依次映射到树状数组的数组中,每次修改,最终输出结果即可

代码:

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

struct Fenwick {
    int n;
    vector<int> tr;
    Fenwick(int _n) { n = _n + 1; tr.assign(n, 0); }
    int lowbit(int x) { return x & -x; }
    void add(int idx, int val) { for (int i = idx; i <= n; i += lowbit(i)) { tr[i] += val; } }
    int query(int idx) {int sum = 0; for (int i = idx; i > 0; i -= lowbit(i)) { sum += tr[i]; } return sum; }
};

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

    int n, m, q;
    cin >> n >> m >> q;
    
    string s;
    cin >> s;
    s = " " + s;

    // 这里并查集可能需要初始化到n + 1,所以开大一些
    vector<int> fa(n + 2, 0); 
    for (int i = 1; i <= n + 1; i++) { fa[i] = i; }
    auto find = [&](auto &&self, int x) -> int {
        return x == fa[x] ? x : fa[x] = self(self, fa[x]);
    };

    Fenwick fenwick(n);

    int siz = 0, cnt1 = count(s.begin(), s.end(), '1');
    vector<int> pos(n + 1, 0);
    for (int i = 1; i <= m; i++) {
        int l, r;
        cin >> l >> r;
        for (int p = find(find, l); p <= r; p = find(find, p)) {
            pos[p] = ++siz;
            if (s[p] == '1') {
                fenwick.add(pos[p], 1);
            }
            fa[p] = p + 1;
        }   
    }

    while (q --) {
        int x;
        cin >> x;
        if (s[x] == '1') {
            cnt1 --; s[x] = '0';
        } else {
            cnt1 ++; s[x] = '1';
        }
        if (pos[x]) {
            fenwick.add(pos[x], (s[x] == '1') ? 1 : -1);
        }
        if (cnt1 >= siz) {
            cout << siz - fenwick.query(siz) << "\n";
        } else {
            cout << cnt1 - fenwick.query(cnt1) << "\n";
        }
    }

    return 0;
}

F. The Boss's Identity

F待补

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值