Codeforces Round 934(Div.1) & (Div.2) 2A~2D(1A~1B)

A.Destroying Bridges(思维)

题意:

nnn个岛屿,编号为1,2,…,n1,2,\ldots,n1,2,,n。最初,每对岛屿都由一座桥连接。因此,一共有n(n−1)2\frac{n(n-1)}{2}2n(n1)座桥。

Everule住在111号岛上,喜欢利用桥梁访问其他岛屿。Dominater有能力摧毁最多kkk座桥梁,以尽量减少Everule可以使用(可能是多座)桥梁到达的岛屿数量。

如果Dominater以最佳方式摧毁桥梁,求Everule可以访问的岛屿(包括岛屿111)的最少数量。

分析:

本题岛屿和桥构成一个完全图,每个点的连边数为n−1n-1n1,要么至少摧毁111号岛连接其他n−1n-1n1座岛的所有n−1n-1n1座桥,这样只能访问111岛,要么所有岛都能访问。

代码:

#include<bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, k;
        cin >> n >> k;
        if (k >= n - 1)
            cout << 1 << endl;
        else
            cout << n << endl;
    }
    return 0;
}

B.Equal XOR(数学)

题意:

给你一个长度为2n2n2n的数组aaa,其中从111nnn的每个整数都出现两次

同时给你一个整数kkk(1≤k≤⌊n2⌋1\leq k\leq\lfloor\frac{n}{2}\rfloor1k2n)。

你需要找到两个长度分别为2k\mathbf{2k}2k的数组lllrrr,使得:

  • lll[a1,a2,…an][a_1,a_2,\ldots a_n][a1,a2,an]的子集†^\dagger
  • rrr[an+1,an+2,…a2n][a_{n+1},a_{n+2},\ldots a_{2n}][an+1,an+2,a2n]的子集
  • lll中元素的按位异或等于rrr中元素的按位异或;换句话说,l1⊕l2⊕…⊕l2k=r1⊕r2⊕…⊕r2kl_1\oplus l_2\oplus\ldots\oplus l_{2k}=r_1\oplus r_2\oplus\ldots\oplus r_{2k}l1l2l2k=r1r2r2k

可以证明至少有一对lllrrr总是存在的。如果有多个解,可以输出其中任意一个。

†^\dagger 序列xxx是序列yyy的子集,如果xxx可以通过删除yyy中的几个元素(可能一个元素也没有或全部元素)并按任意顺序重新排列而得到。例如,[3,1,2,1][3,1,2,1][3,1,2,1][1,2,3][1,2,3][1,2,3][1,1][1,1][1,1][3,2][3,2][3,2][1,1,2,3][1,1,2,3][1,1,2,3]的子集,但[4][4][4][2,2][2,2][2,2]不是[1,1,2,3][1,1,2,3][1,1,2,3]的子集。

分析:

对于每个数字有三种情况:

  1. 只出现在(1,n)(1,n)(1,n)
  2. 只出现在(n+1,2n)(n+1,2n)(n+1,2n)
  3. 一个出现在(1,n)(1,n)(1,n),另一个出现在(n+1,2n)(n+1,2n)(n+1,2n)

前两种情况数目是相同的。我们先用前两种成对分别填入l,rl,rl,r中,不够的再用第三种补即可。

代码:

#include<bits/stdc++.h>

using namespace std;

void solve() {
    int n, k;
    cin >> n >> k;
    vector<int> a(2 * n + 2);
    for (int i = 1; i <= 2 * n; i++) {
        cin >> a[i];
    }
    vector<int> l, r;
    vector<int> vis(2 * n + 5);
    for (int i = 1; i <= n; i++) {
        vis[a[i]] += 1;
    }
    for (int i = n + 1; i <= 2 * n; i++) {
        vis[a[i]] += 2;
    }
    vector<int> ls, rs, t;
    for (int i = 1; i <= n; i++) {
        if (vis[a[i]] == 3) {
            t.push_back(a[i]);
        } else if (vis[a[i]] == 2) {
            ls.push_back(a[i]);
        }
    }
    for (int i = n + 1; i <= 2 * n; i++) {
        if (vis[a[i]] == 4) {
            rs.push_back(a[i]);
        }
    }
    sort(ls.begin(), ls.end());
    ls.erase(unique(ls.begin(), ls.end()), ls.end());
    sort(rs.begin(), rs.end());
    rs.erase(unique(rs.begin(), rs.end()), rs.end());
    for (int i = 0; i < min(ls.size(), rs.size()) && l.size() < 2 * k; i++) {
        l.push_back(ls[i]);
        l.push_back(ls[i]);
        r.push_back(rs[i]);
        r.push_back(rs[i]);
    }
    for (int i = 0; i < t.size() && l.size() < 2 * k; i++) {
        l.push_back(t[i]);
        r.push_back(t[i]);
    }
    for (auto x: l) {
        cout << x << " ";
    }
    cout << endl;
    for (auto x: r) {
        cout << x << " ";
    }
    cout << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

C.MEX Game 1(思维)(DIV1-A)

题意:

爱丽丝和鲍勃在大小为nnn的数组aaa上进行另一场博弈。爱丽丝从一个空数组ccc开始。双方轮流下棋,爱丽丝先开始。

轮到爱丽丝时,她从aaa中选取一个元素,将其追加到ccc中,然后从aaa中删除。

轮到鲍勃时,他从aaa中选取一个元素,然后从aaa中删除。

当数组aaa为空时,游戏结束。游戏的分数定义为ccc的MEX †^\dagger 。爱丽丝希望最大化得分,而鲍勃希望最小化得分。如果双方都以最佳状态进行游戏,求游戏的最终得分。

†^\dagger整数数组的MEX⁡\operatorname{MEX}MEX(最小不等式)定义为数组中不出现的最小非负整数。例如

  • [2,2,1][2,2,1][2,2,1]MEX⁡\operatorname{MEX}MEX000,因为000不属于数组。
  • [3,1,0,1][3,1,0,1][3,1,0,1]MEX⁡\operatorname{MEX}MEX222,因为000111属于数组,而222不属于数组。
  • [0,3,1,2][0,3,1,2][0,3,1,2]MEX⁡\operatorname{MEX}MEX444,因为000111222333属于数组,而444不属于数组。

分析:

出现次数大于111次的,无论先后手爱丽丝肯定都能抢到。

由于先手的缘故,爱丽丝可以保证将一个出现次数为111的数拿到手,显然开局拿最小的且出现次数为111的数是最优的,所以我们按照MEX⁡\operatorname{MEX}MEX,从小到大优先拿出现次数为111的数,然后从小到大找第一个拿不到的数即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 5);
    map<int, int> tmp;
    vector<bool> vis(n + 5);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        tmp[a[i]]++;
    }
    for (auto t: tmp) {
        if (t.second >= 2) {
            vis[t.first] = true;
        }
    }
    int cur = 0;
    while (vis[cur]) {
        cur++;
    }
    for (int i = 1; i <= n; i++) {
        while (vis[cur]) {
            cur++;
        }
        if (tmp[cur] == 1) {
            tmp[cur]--;
            vis[cur] = true;
        } else {
            break;
        }
        while (vis[cur]) {
            cur++;
        }
        if (tmp[cur] == 1) {
            tmp[cur]--;
        }
    }
    LL ans = 0;
    while (vis[ans]) {
        ans++;
    }
    cout << ans << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

D.Non-Palindromic Substring(思维、贪心)(DIV1-B)

题意:

如果至少存在一个长度为kkk的子串†^\dagger不是回文‡^\ddagger,则称字符串ttt为好kkk字符串。令f(t)f(t)f(t)表示所有kkk的值之和,使得字符串ttt是好kkk字符串。

给你一个长度为nnn的字符串sss。回答qqq个问题:

  • 给定lllrrr(l<rl\lt rl<r),求f(slsl+1…sr)f(s_ls_{l+1}\ldots s_r)f(slsl+1sr)的值。

†^\dagger 字符串zzz的子串是来自zzz的连续字符段。例如,“defor\mathtt{defor}defor”、"code\mathtt{code}code“和”o\mathtt{o}o“都是”codeforces\mathtt{codeforces}codeforces“的子串,而”codes\mathtt{codes}codes“和”aaa\mathtt{aaa}aaa"不是。

‡^\ddagger 回文字符串是指前后读法相同的字符串。例如,字符串"z\texttt{z}z“、”aa\texttt{aa}aa“和”tacocat\texttt{tacocat}tacocat“是回文字符串,而”codeforces\texttt{codeforces}codeforces“和”ab\texttt{ab}ab"不是。

分析:

反着考虑全为回文串需满足哪些情况:

  • k=1k=1k=1 没有限制
  • k=2k=2k=2 整个区间全部相同
  • k=3k=3k=3 交替出现
  • k>3k\gt3k>3 递推一下可以发现奇数需交替排列或全相等,偶数只能全相等。

特殊情况,若k=r−l+1k=r-l+1k=rl+1,那么只要整段不回文,就有r−l+1r-l+1rl+1的贡献

判断区间是否回文可以使用马拉车算法或者字符串哈希。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

vector<LL> manacher_odd(string s) {
    LL n = s.size();
    s = "$" + s + "^";
    vector<LL> p(n + 2);
    LL l = 1, r = 1;
    for (LL i = 1; i <= n; i++) {
        p[i] = max(0ll, min(r - i, p[l + (r - i)]));
        while (s[i - p[i]] == s[i + p[i]]) {
            p[i]++;
        }
        if (i + p[i] > r) {
            l = i - p[i], r = i + p[i];
        }
    }
    return vector<LL>(begin(p) + 1, end(p) - 1);
}

vector<LL> manacher(string s) {
    string t;
    for (auto c: s) {
        t += string("#") + c;
    }
    auto res = manacher_odd(t + "#");
    return vector<LL>(begin(res) + 1, end(res) - 1);
}

void Solve() {
    LL n, q;
    cin >> n >> q;
    string s;
    cin >> s;
    auto v = manacher(s);
    for (auto &x: v)
        x--;
    set<LL> s1, s2;
    for (LL i = 0; i < n - 1; i++) {
        if (s[i] != s[i + 1])
            s1.insert(i);
        if (i != n - 1 && s[i] != s[i + 2])
            s2.insert(i);
    }

    while (q--) {
        LL l, r;
        cin >> l >> r;
        l--;
        r--;

        if (l == r) {
            cout << 0 << endl;
            continue;
        }
        LL len = r - l + 1;

        LL ans;
        auto it = s1.lower_bound(l);
        if (it == s1.end() || (*it) >= r) {
            ans = 0;
        } else {
            it = s2.lower_bound(l);
            if (it == s2.end() || (*it) >= r - 1) {
                ans = ((len - 1) / 2) * (((len - 1) / 2) + 1);
            } else {
                ans = len * (len - 1) / 2 - 1;
            }
        }

        if (v[l + r] < (r - l + 1))
            ans += len;

        cout << ans << endl;
    }
}

int main() {
    LL t;
    cin >> t;
    while (t--) {
        Solve();
    }
    return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值