首页 > 其他分享 >Codeforces Round #830 (Div. 2) A-D

Codeforces Round #830 (Div. 2) A-D

时间:2022-10-26 18:34:30浏览次数:80  
标签:830 cin int 复杂度 元素 pos long Codeforces Div

比赛链接

A

题解

知识点:贪心,数论。

先求出序列最大公约数 \(d\) ,如果为 \(1\) 直接输出 \(0\) 。

否则,尝试用最后一个数操作, \(gcd(d,n) = 1\) 则可以,花费为 \(1\) 。

否则,用倒数第二个数操作,\(gcd(d,n-1) = 1\) (不必担心 \(n-1 = 0\) ,因为此时上一步一定成功),花费为 \(2\) 。

否则,用倒数两个数操作,一定成功,因为 \(gcd(n-1,n)=1\) ,花费为 \(3\) 。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int a[27];
bool solve() {
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) cin >> a[i];
    int d = a[1];
    for (int i = 2;i <= n;i++) d = __gcd(a[i], d);
    if (d == 1) cout << 0 << '\n';
    else if (__gcd(d, n) == 1) cout << 1 << '\n';
    else if (__gcd(d, n - 1) == 1) cout << 2 << '\n';
    else cout << 3 << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

B

题解

知识点:贪心。

显然左侧已经排好的不用管,从第一段 \(1\) 开始记录后面一共分成的段数 \(cnt\)(如 0000|111|00|1|0|1|0 有 \(6\) 段)。若 \(cnt > 1\) ,则从第一段开始使用操作,每次可以将一段变为 \(0\) (后面也会改变),直到最后一段不用更改,一共操作 \(cnt-1\) 次。另外,如果 \(cnt = 0\) ,答案也是 \(0\) 。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

bool solve() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    s = "?" + s;
    bool st = 0;
    int cnt = 0;
    for (int i = 1;i <= n;i++) {
        if (s[i] != st + '0') {
            cnt++;
            st ^= 1;
        }
    }
    cout << max(cnt - 1, 0) << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

C

题解

知识点:枚举,双指针,位运算,前缀和,贪心。

因为异或相当于不进位的加法,那么前缀和减去前缀异或和一定是非减的,于是一个贪心的结论:最大值一定是 \(f(L_i,R_i)\) 的值。接下来需要用这个值,求一个最小区间。

为了方便区间运算,我们预处理一个前缀和,以及一个前缀异或和。

可以证明,最多删除 \(31\) 个非 \(0\) 元素,否则区间值必然减小。因为在 int 范围内, \(31\) 个二进制位,最多能分配给 \(31\) 个数一个不同为值的 \(1\) ,这样能使这 \(31\) 个数对答案没有贡献,实际情况不会超过 \(31\) ,因此我们放心大胆的枚举端点就行了。我们只需要考虑非 \(0\) 点,遍历一遍存一下非 \(0\) 点坐标即可。左端点从左往右,内循环右端点从右往左,区间等于最大值的如果长度更小就更新答案。

另外,需要特判没有非 \(0\) 元素的情况,此时直接输出 \(L,L\) 即可。

时间复杂度 \(O(n+q\log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int a[100007];
ll sum[100007], sumx[100007];
bool solve() {
    int n, q;
    cin >> n >> q;
    for (int i = 1;i <= n;i++) cin >> a[i];
    vector<int> pos;
    for (int i = 1;i <= n;i++) {
        sum[i] = sum[i - 1] + a[i];
        sumx[i] = sumx[i - 1] ^ a[i];
        if (a[i]) pos.push_back(i);
    }
    while (q--) {
        int L, R;
        cin >> L >> R;
        int l = lower_bound(pos.begin(), pos.end(), L) - pos.begin();
        int r = upper_bound(pos.begin(), pos.end(), R) - pos.begin() - 1;
        auto get = [&](int _l, int _r) {return sum[_r] - sum[_l - 1] - (sumx[_r] ^ sumx[_l - 1]);};
        ll val = get(L, R);
        int ansl = 0, ansr = n + 1;
        for (int i = l;i <= r;i++) {
            if (get(pos[i], pos[r]) < val) break;//可以证明无论左右端点至多删除31个非0元素对答案没有影响(0,2,4,8,...鸽巢原理)
            for (int j = r;j >= i;j--) {
                if (get(pos[i], pos[j]) < val) break;
                if (pos[j] - pos[i] + 1 < ansr - ansl + 1) {
                    ansl = pos[i];
                    ansr = pos[j];
                }
            }
        }
        if (ansr - ansl + 1 > n) cout << L << ' ' << L << '\n';
        else cout << ansl << ' ' << ansr << '\n';
    }
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

D1

题解

知识点:STL,枚举。

用一个 set<ll> vis 维护出现过的元素,再用一个 map<ll,ll> last 维护某个元素上一次的询问结果,下一次询问这个元素时直接从上一次结果开始。

每个元素 \(i\) 只经过其倍数一次,共 \(\dfrac{n}{i}\) 次。所有元素次数之和 \(O(\sum \dfrac{n}{i}) = O(n \log n)\) 。

时间复杂度 \(O(q \log^2 q)\)

空间复杂度 \(O(q)\)

代码

#include <bits/stdc++.h>
#define ll long long


using namespace std;

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int q;
    cin >> q;
    set<ll> vis;
    map<ll, ll> last;
    while (q--) {
        char op;
        ll x;
        cin >> op >> x;
        if (op == '+') {
            vis.insert(x);
        }
        else {
            if (!last.count(x)) last[x] = x;
            while (vis.count(last[x])) last[x] += x;
            cout << last[x] << '\n';
        }
    }
    return 0;
}

D2

题解

知识点:STL,枚举。

因为存在删除已存在元素的操作,这意味着我们之前得到的答案在未来可能不适用。

因此需要对每个已询问过的数字维护一个已删除集合 map<ll,set<ll>> del ,来得到目前某个元素的最大询问结果之前,是否有在序列中被删除的倍数。

对于已删除集合的维护,需要对每个询问过程中遍历到的且存在于序列中的倍数维护一个影响列表 map<ll,vector<ll>> chg ,来得到修改序列某个元素的状态时,哪些询问过的元素的已删除集合会被修改。

如此我们就维护了带删除求 mex 的数据结构。

时间复杂度 \(O(q \log ^2 q)\)

空间复杂度 \(O(q\log q)\)

代码

#include <bits/stdc++.h>
#define ll long long


using namespace std;

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int q;
    cin >> q;
    set<ll> vis;//序列中存在的元素
    map<ll, ll> last;//某元素最大询问结果:记录需要维护的数据上界,超出最大询问结果的不需要考虑
    map<ll, set<ll>> del;//某元素的已删除集合:最大询问结果内,目前不存在于序列中的倍数
    map<ll, vector<ll>> chg;//某元素的影响列表:更改序列中某元素状态,已删除集合将发生变化的元素列表
    while (q--) {
        char op;
        ll x;
        cin >> op >> x;
        if (op == '+') {
            vis.insert(x);
            for (auto y : chg[x]) del[y].erase(x);//删除受x影响元素的已删除集合中的x,因为x已存在
        }
        else if (op == '-') {
            vis.erase(x);
            for (auto y : chg[x]) del[y].insert(x);//增加受x影响元素的已删除集合中的x,因为x已删除
        }
        else {
            if (!last.count(x)) last[x] = x;//新增已询问元素
            if (del[x].size()) cout << *del[x].begin() << '\n';//已删除集合存在元素,优先使用
            else {//考虑最大询问结果是否可行,否则扩大最大询问结果
                while (vis.count(last[x])) {
                    chg[last[x]].push_back(x);//已存在的x的倍数,在未来可能会被修改状态,影响x的结果
                    last[x] += x;
                }
                cout << last[x] << '\n';
            }
        }
    }
    return 0;
}

标签:830,cin,int,复杂度,元素,pos,long,Codeforces,Div
From: https://www.cnblogs.com/BlankYang/p/16829557.html

相关文章

  • Educational Codeforces Round 109 (Rated for Div. 2) D
    D.Armchairs我们发现性质这前面的0显然是给第一个1匹配而不会前面0的给第二个后面的给第一个显然不优有了这个性质我们就可以通过0来做文章要是这个位置是0我们显......
  • Ye Yuan-2019-DiverseTrajectoryForecastingWithDeterminantalPointProcesses
    #DiverseTrajectoryForecastingwithDeterminantalPointProcesses#paper1.paper-info1.1MetadataAuthor::[[YeYuan]],[[KrisKitani]]作者机构::Carne......
  • Codeforces Round #715 (Div. 2) C
    C.TheSportsFestival观察发现我们显然选择一个数字开始后我们拿周围的数字显然存在最优解(sort过)这样就很金典了n=2000我们显然可以暴力区间dp然后将转移只用从拿......
  • Codeforces Round #697 (Div. 3) D
    D.CleaningthePhone金典贪心吧先sort从大到小考虑12两种情况显然要是我们当前now+最大的一个1那我们就直接break了继续我们知道了我们现在+最大的一个1不够我们......
  • 固定div尺寸 图片适应不变形处理样式
    .div-image{   width:200px;   height:132px;   overflow:hidden;   display:flex;   align-items:center;   justify-......
  • Solution: CF Round #830 (Div. 2) D1&D2 Balance
    FirstCFroundatCambridge.SolvedA,B,D1intheround.Droppedfrompurpletoblue...Stillalongwaytogo...Solution:CFRound#830(Div.2)D1&D2Balanc......
  • Codeforces Round #735 (Div. 2) C
    C.Mikasa显然我们应该用log或者O(1)的时间来回答一个ans当n>m时显然我们不能n^m==0所以直接cout0(1)我们知道的是n^i=?那么显然n^?=i(2)然后对于每一个n^i的值是不同的......
  • Codeforces Round #729 (Div. 2) B. Plus and Multiply (数论/暴力)
    https://codeforces.com/contest/1542/problem/B题目大意:有一个无限集合生成如下:1在这个集合中。如果x在这个集合中,x⋅a和x+b都在这个集合中。给定nab,问我们n......
  • Codeforces Round #743 A
    A.Book拓扑序我们一眼就能看出来主要是如何求读书的遍数我最开始想的就是把拓扑序处理出来类似于要几遍上升序列能把他全部覆盖显然求一遍不上升子序列即可但是我......
  • Codeforces 1672 F1. Array Shuffling
    题意给一个n个数的数列a,a[i]<=n定义一个操作:每次可以交换任意位置的两个值;定义最优操作:对于任意一个原数列的一组排列,使其通过尽可能少的操作变回原数列;求构造一组原......