问答媒体

 找回密码
 立即注册
快捷导航
搜索
热搜: 活动 交友 discuz
查看: 127|回复: 17

Codeforces Round #825 (Div. 2) A

[复制链接]

4

主题

5

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2022-11-27 14:33:20 | 显示全部楼层 |阅读模式
赛时搞出了D,C2赛时有一处输出忘记换行了,惨...
A Make A Equal to B

代码实现

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
using LL = long long;
const int maxn = 105;
int a[maxn], b[maxn];

int main(){
        cin.tie(0);
        cout.tie(0);
        ios::sync_with_stdio(0);

        int T;
        cin >> T;
        while(T--){
                int n;
                cin >> n;
                for(int i = 1; i <= n; i++) cin >> a;
                for(int i = 1; i <= n; i++) cin >> b;
                int cnta = count(a + 1, a + n + 1, 1);
                int cntb = count(b + 1, b + n + 1, 1);
                int c = 0;
                for(int i = 1; i <= n; i++) c += (a != b);
                cout << min(c, abs(cnta - cntb) + 1) << '\n';
        }

}
B Playing with GCD

代码实现

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
using LL = long long;
const int maxn = 1e5 + 5;
int a[maxn];

int main(){

        cin.tie(0);
        cout.tie(0);
        ios::sync_with_stdio(0);

        int T;
        cin >> T;
        while(T--){
                int n;
                cin >> n;
                for(int i = 1; i <= n; i++) cin >> a;
                bool success = 1;
                for(int i = 2; i <= n - 1; i++){
                        if (a % __gcd(a[i - 1], a[i + 1])){
                                success = 0;
                                break;
                        }
                }
                cout << (success ? "YES" : "NO") << '\n';
        }

}
C1 Good Subarrays (Easy Version)

分析

思路是固定左端点,求能到达的右端点.
把位置按照a_i - i排序,然后双指针搞一下即可.从左到右遍历,利用双指针把满足当前位置要求的元素填进去,每次右端点就是往右第一个还没填的位置,这个位置也是单调的可以用双指针暴力计算.
代码实现

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
using LL = long long;
const int maxn = 2e5 + 5;
int a[maxn], id[maxn];
bool v[maxn];

int main(){

        cin.tie(0);
        cout.tie(0);
        ios::sync_with_stdio(0);

        int T;
        cin >> T;
        while(T--){
                int n;
                cin >> n;
                for(int i = 1; i <= n; i++) cin >> a, id = i, v = 0;
                v[n + 1] = 0;
                sort(id + 1, id + n + 1, [&](int x, int y){
                        return a[x] - x > a[y] - y;
                });
                LL ans = 0;
                for(int i = 1, j = 0, r = 1; i <= n; i++){
                        while(j + 1 <= n && a[id[j + 1]] - id[j + 1] + i - 1 >= 0){
                                j++;
                                v[id[j]] = 1;
                        }
                        while(v[r]) r++;
                        ans += r - i;
                }
                cout << ans << '\n';
        }

}
C2 Good Subarrays (Hard Version)

分析

这道题赛时没过,然后赛后一看错误样例居然是因为输出答案没有换行......吐血
首先我们把答案算出来,对于每次修改我们计算增量.
首先我们预处理出对于每个左端点,右端点最远可以到哪里,因为这个位置可能可以被修改,所以我们还需要在处理以下如果这个位置能满足要求的话右端点最远能到什么位置,但是这样就无法使用双指针(因为势能均摊失效)去做了,我们可以用线段树二分去做,线段树二分找序列中第一个0出现的位置是经典操作,有模板的话套个模板就行了.
对于每次修改

  • 当x > a[p],显然这种情况最后答案会增加,我们可以通过右端点二分出受影响的左右边界,然后通过预处理出的数组前缀和(即假设不满足要求的第一个位置满足要求后右端点最远能到达什么位置)就可以快速计算增量.
  • 当x < a[p],这种情况答案会减少,我们同样可以二分出影响的区间左右边界,然后利用右端点数组的前缀和可以快速求解(受影响的区间r之和减去区间长度乘位置p,可以理解为所有右端点本来大于p的位置右端点都变成了p).
代码实现

#include<iostream>
#include<cstring>
#include<vector>
#include<numeric>
#include<functional>
#include<algorithm>
using namespace std;
using LL = long long;
const int maxn = 2e5 + 5;
int a[maxn], id[maxn];
int R[maxn];
LL s[maxn], sr[maxn];

struct Info {
        int v;
    Info() : v(1) {}
        Info(int v) : v(v) {}
};

Info operator+(const Info &a, const Info &b){
        return min({a.v, b.v});
}

template<class Info, class Merge = plus<Info>>
struct SegmentTree{
    const int n;
    const Merge merge;
    vector<Info> info;

    SegmentTree(int n) : n(n), merge(Merge()), info((n << 2) + 1){}
    SegmentTree(vector<Info> init) : SegmentTree((int)init.size()){
        function<void(int, int, int)> build = [&](int p, int l, int r){
            if (l == r){
                info[p] = init[l - 1];
                return;
            }
            int m = (l + r) / 2;
            build(2 * p, l, m);
            build(2 * p + 1, m + 1, r);
            pull(p);
        };
        build(1, 1, n);
    }

    void pull(int p){
        info[p] = merge(info[2 * p], info[2 * p + 1]);
    }

    void modify(int p, int l, int r, int x, const Info &v){
        if (l == r){
            info[p] = v;
            return;
        }
        int m = (l + r) / 2;
        if (x <= m){
            modify(2 * p, l, m, x, v);
        }
                else{
            modify(2 * p + 1, m + 1, r, x, v);
        }
        pull(p);
    }

    void modify(int p, const Info &v){
        modify(1, 1, n, p, v);
    }

    Info query(int p, int l, int r, int x, int y){
        if (l > y || r < x){
            return Info();
        }
        if (l >= x && r <= y){
            return info[p];
        }
        int m = (l + r) / 2;
        return merge(query(2 * p, l, m, x, y), query(2 * p + 1, m + 1, r, x, y));
    }

    Info query(int l, int r){
        return query(1, 1, n, l, r);
    }

    int find_first(int p, int l, int r, int L, int R, const function<bool(const Info&)> &f, Info &pre){
        if (l >= L && r <= R){
            if (!f(merge(pre, info[p]))){
                pre = merge(pre, info[p]);
                return r + 1;
            }
            if (l == r) return r;
            int m = (l + r) / 2;
            int res;
            if (f(merge(pre, info[2 * p]))){
                res = find_first(2 * p, l, m, L, R, f, pre);
            }
            else{
                pre = merge(pre, info[2 * p]);
                res = find_first(2 * p + 1, m + 1, r, L, R, f, pre);
            }
            return res;
        }
        int m = (l + r) / 2;
        int res = m + 1;
        if (L <= m){
            res = find_first(2 * p, l, m, L, R, f, pre);
        }
        if (R > m && res == m + 1){
            res = find_first(2 * p + 1, m + 1, r, L, R, f, pre);
        }
        return res;
    }

    int find_first(int l, int r, const function<bool(const Info&)> &f){
        Info pre = Info();
        return find_first(1, 1, n, l, r, f, pre);
    }

};

int main(){

        cin.tie(0);
        cout.tie(0);
        ios::sync_with_stdio(0);

        int n, m;
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> a, id = i;
        sort(id + 1, id + n + 1, [&](int x, int y){
                return a[x] - x > a[y] - y;
        });

        const function<bool(const Info&)> f = [&](const Info &info){
                return info.v == 0;       
        };

        SegmentTree<Info> seg(vector<Info>(n, Info(0)));
        LL ans = 0;
        for(int i = 1, j = 0; i <= n; i++){
                while(j + 1 <= n && a[id[j + 1]] - id[j + 1] + i - 1 >= 0){
                        j++;
                        seg.modify(id[j], {1});
                }
                int r = seg.find_first(i, n, f);
                R = r;
                ans += r - i;
                if (r <= n){
                        seg.modify(r, {1});
                        s = seg.find_first(i, n, f) - r;
                        seg.modify(r, {0});
                }
        }
        for(int i = 1; i <= n; i++) s += s[i - 1];
        for(int i = 1; i <= n; i++) sr = sr[i - 1] + R;
        cin >> m;
        while(m--){
                int p, x;
                cin >> p >> x;
                if (x == a[p]){
                        cout << ans << '\n';
                }
                else if (x > a[p]){
                        int Ri = upper_bound(R + 1, R + p, p) - R - 1;
                        int Le = lower_bound(R + 1, R + p, p) - R;
                        Le = max(Le, max(1, p - x + 1));
                        if (Le > Ri) cout << ans << '\n';
                        else cout << ans + s[Ri] - s[Le - 1] << '\n';
                }
                else{
                        int Le = upper_bound(R + 1, R + p, p) - R;
                        int Ri = max(0, p - x);
                        if (Le > Ri) cout << ans << '\n';
                        else cout << ans - (sr[Ri] - sr[Le - 1] - 1LL * (Ri - Le + 1) * p) << '\n';
                }
        }

}
D Equal Binary Subsequences

分析

首先如果0或者1出现了奇数次肯定是无解的.
其他情况都是有解的,我们可以考虑把原串变成a_{2k - 1} = a_{2k}的形式,例如0011001111,这样只要交替取就能轻松构造出答案.
首先先找到所有a_{2k - 1} \ne a_{2k}的位置记录下来我们只操作这些位置,很显然这些位置要么是01要么10,并且总数肯定是偶数个,我们要做的是把这些位置变为11或者00,假设这些位置为p_1, p_2, p_3, p_4 \cdots(p_i = 01/10)所以其实我们只需要把奇数位置的的1换到偶数位置去,偶数位置的0换到奇数位置去即可.比如01, 10,我们就把10的1换到前面去,变成11, 00,更长的序列也一样,实现方法见代码.
这个题目的解法其实非常简单,主要难度在于思维,在场上我第一感觉也是先想办法check一个序列能否满足题目的要求,但是发现不带修都很难做,不太符合Div.2D的难度,然后才转变思路去做构造,只要能想到构造出的最终串是什么样子这个问题就迎刃而解了.
做这类题目的话我们可以发现其实题目最终的解法没有任何价值,思考这个题目的过程才有价值.
代码实现

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
using LL = long long;

int main(){

        cin.tie(0);
        cout.tie(0);
        ios::sync_with_stdio(0);

        int T;
        cin >> T;
        while(T--){
                int n; string s;
                cin >> n >> s;
                int cnt = count(s.begin(), s.end(), '1');
                if (cnt % 2){
                        cout << -1 << '\n';
                        continue;
                }
                s = " " + s;
                vector<int> v, ans;
                for(int i = 1; i <= 2 * n; i += 2)
                        if (s != s[i + 1]) v.push_back(i);
                for(int i = 0; i < v.size(); i++){
                        int pos = v;
                        if ((i & 1) == s[pos] - '0') ans.push_back(pos);
                        else ans.push_back(pos + 1);
                }
                cout << ans.size();
                for(auto x : ans) cout << ' ' << x;
                cout << '\n';
                for(int i = 1; i <= 2 * n; i += 2)
                        cout << i << ' ';
                cout << '\n';
        }

}
本文使用 Zhihu On VSCode 创作并发布
回复

使用道具 举报

0

主题

9

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2022-11-27 14:33:28 | 显示全部楼层
太快了[赞同][赞同][赞同]
回复

使用道具 举报

0

主题

7

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-11-27 14:34:09 | 显示全部楼层
[可怜]
回复

使用道具 举报

3

主题

6

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-11-27 14:35:00 | 显示全部楼层
难受,最近也一直在练习,但是感觉打cf越来越差,近两场打的都不好,这次b题就被卡住了[捂脸]难受
回复

使用道具 举报

0

主题

8

帖子

15

积分

新手上路

Rank: 1

积分
15
发表于 2022-11-27 14:35:10 | 显示全部楼层
这个B其实我也没办法证明正确性,当时还想了好一会,但是也没想到合理的证明就直接交了
回复

使用道具 举报

0

主题

4

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-11-27 14:35:46 | 显示全部楼层
没有啥方法是立竿见影的,尤其是打cf,只有长期才能看到明显的提升
回复

使用道具 举报

0

主题

6

帖子

9

积分

新手上路

Rank: 1

积分
9
发表于 2022-11-27 14:36:17 | 显示全部楼层
写完3题就开摆了,d题完全不会[大哭]
回复

使用道具 举报

1

主题

2

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2022-11-27 14:36:39 | 显示全部楼层
这个d题最后也就过了不到四百人,属于div2比较难的d
回复

使用道具 举报

2

主题

3

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2022-11-27 14:37:17 | 显示全部楼层
我也是在想怎么check一个合法序列完全想歪了[捂脸]
回复

使用道具 举报

0

主题

3

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-11-27 14:38:17 | 显示全部楼层
我也是…[捂脸]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver| 手机版| 小黑屋| 问答媒体

GMT+8, 2025-7-7 15:58 , Processed in 0.110879 second(s), 23 queries .

Powered by Discuz! X3.4

Copyright © 2020, LianLian.

快速回复 返回顶部 返回列表