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

A.Destroying Bridges(思维)

题意:

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

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

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

分析:

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

代码:

#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(数学)

题意:

给你一个长度为 2 n 2n 2n的数组 a a a,其中从 1 1 1 n n n的每个整数都出现两次

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

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

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

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

† ^\dagger 序列 x x x是序列 y y y的子集,如果 x x x可以通过删除 y y y中的几个元素(可能一个元素也没有或全部元素)并按任意顺序重新排列而得到。例如, [ 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 , 2 n ) (n+1,2n) (n+1,2n)
  3. 一个出现在 ( 1 , n ) (1,n) (1,n),另一个出现在 ( n + 1 , 2 n ) (n+1,2n) (n+1,2n)

前两种情况数目是相同的。我们先用前两种成对分别填入 l , r l,r l,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)

题意:

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

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

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

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

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

  • [ 2 , 2 , 1 ] [2,2,1] [2,2,1] MEX ⁡ \operatorname{MEX} MEX 0 0 0,因为 0 0 0不属于数组。
  • [ 3 , 1 , 0 , 1 ] [3,1,0,1] [3,1,0,1] MEX ⁡ \operatorname{MEX} MEX 2 2 2,因为 0 0 0 1 1 1属于数组,而 2 2 2不属于数组。
  • [ 0 , 3 , 1 , 2 ] [0,3,1,2] [0,3,1,2] MEX ⁡ \operatorname{MEX} MEX 4 4 4,因为 0 0 0 1 1 1 2 2 2 3 3 3属于数组,而 4 4 4不属于数组。

分析:

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

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

代码:

#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)

题意:

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

给你一个长度为 n n n的字符串 s s s。回答 q q q个问题:

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

† ^\dagger 字符串 z z z的子串是来自 z z z的连续字符段。例如,“ d e f o r \mathtt{defor} defor”、" c o d e \mathtt{code} code“和” o \mathtt{o} o“都是” c o d e f o r c e s \mathtt{codeforces} codeforces“的子串,而” c o d e s \mathtt{codes} codes“和” a a a \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 = 1 k=1 k=1 没有限制
  • k = 2 k=2 k=2 整个区间全部相同
  • k = 3 k=3 k=3 交替出现
  • k > 3 k\gt3 k>3 递推一下可以发现奇数需交替排列或全相等,偶数只能全相等。

特殊情况,若 k = r − l + 1 k=r-l+1 k=rl+1,那么只要整段不回文,就有 r − l + 1 r-l+1 rl+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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

在这里插入图片描述

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值