Codeforces Round 932(Div.2) A~E

A.Entertainment in MAC(字符串)

题意:

恭喜你,你被硕士援助中心录取了!但是,你在课堂上感到非常无聊,于是你给自己想了一个游戏。

给你一个字符串 s s s和一个偶数整数 n n n。你可以对它进行两种运算:

  1. 将字符串反向并 s s s添加到字符串 s s s的末尾(例如,如果 s = c p m s=cpm s=cpm,那么在执行操作之后字符串变为 s = c p m m p c s=cpmmpc s=cpmmpc)。
  2. 将当前字符串 s s s倒转(例如,如果 s = c p m s=cpm s=cpm,则在执行操作后变为 s = m p c s=mpc s=mpc)。

确定在应用了恰好 n n n次操作后可以得到的字典序最小的 † ^{\dagger} 字符串。请注意,可以按照任意顺序执行不同类型的操作,但必须总共执行恰好 n n n次操作。

† ^{\dagger} 当且仅当以下条件之一成立时,字符串 a a a在词法上小于字符串 b b b

  • a a a b b b的前缀,但是 a ≠ b a\ne b a=b
  • a a a b b b不同的第一个位置上,字符串 a a a的字母在字母表中出现的时间早于 b b b中的相应字母。

分析:

我们注意到当反转字符串后如果字典序减小了,那么就不会再执行反转操作,而是将反转后的字符串拼接(这样一定会构造出一个回文串),那么之后的操作就没有任何意义了,将 n n n次的操作化简为 2 2 2次。

代码:

#include<bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        string s, ans;
        cin >> s;
        ans = min(s, string(s.rbegin(), s.rend()) + s);
        cout << ans << endl;
    }
    return 0;
}

B.Informatics in MAC(区间)

题意:

在硕士援助中心, N y a m − N y a m Nyam-Nyam NyamNyam接受了一项信息学方面的家庭作业。

有一个长度为 n n n的数组 a a a,你想把它分成 k > 1 k \gt 1 k>1个子段 † ^{\dagger} ,使每个子段上的 MEX ⁡ ‡ \operatorname{MEX}^{\ddagger} MEX都等于相同的整数。

请帮助 N y a m − N y a m Nyam-Nyam NyamNyam找到合适的除法,或者确定不存在合适的除法。

† ^{\dagger} 将数组划分为 k k k个子数段的定义是 k k k对整数 ( l 1 , r 1 ) , ( l 2 , r 2 ) , … , ( l k , r k ) (l_1,r_1),(l_2,r_2),\ldots,(l_k,r_k) (l1,r1),(l2,r2),,(lk,rk),使得 l i ≤ r i l_i\le r_i liri和每个 1 ≤ j ≤ k − 1 1\le j\le k-1 1jk1 l j + 1 = r j + 1 l_{j+1}=r_j+1 lj+1=rj+1,以及 l 1 = 1 l_1=1 l1=1 r k = n r_k=n rk=n。这些数对表示子段本身。

数组中的 ‡ MEX ⁡ ^{\ddagger}\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不属于数组。

分析:

如果有解,一定能只划分为两个区间。如果两个区间的 MEX ⁡ \operatorname{MEX} MEX相同,那么合并了也是相同的。将问题转化为求前缀和后缀那个点的 MEX ⁡ \operatorname{MEX} MEX相同。遍历并维护 MEX ⁡ \operatorname{MEX} MEX即可。

代码:

#include<bits/stdc++.h>

using namespace std;

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1), al(n + 1), ar(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        ar[a[i]]++;
    }
    int l, r;
    l = r = 0;
    while (ar[r] > 0) {
        r++;
    }
    for (int i = 1; i < n; i++) {
        al[a[i]]++;
        ar[a[i]]--;
        if (ar[a[i]] == 0) {
            if (r > a[i]) {
                r = a[i] + 1;
            }
        }
        while (al[l] > 0) {
            l++;
        }
        while (r > 0 && ar[r - 1] == 0) {
            r--;
        }
        if (l == r) {
            cout << 2 << endl;
            cout << 1 << ' ' << i << endl;
            cout << i + 1 << ' ' << n << endl;
            return;
        }
    }
    cout << -1 << endl;
}

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

C.Messenger in MAC(贪心)

题意:

一个APP计划进行更新,开发人员希望在更新中优化显示给用户的信息集。目前共有 n n n条信息。每条信息由两个整数 a i a_i ai b i b_i bi表示。阅读数字为 p 1 , p 2 , … , p k p_1,p_2,\ldots,p_k p1,p2,,pk 1 ≤ p i ≤ n 1\le p_i\le n 1pin,所有 p i p_i pi都是不同的)的信息所花费的时间由公式计算得出:

∑ i = 1 k a p i + ∑ i = 1 k − 1 ∣ b p i − b p i + 1 ∣ \Large\sum_{i=1}^{k}a_{p_i}+\sum_{i=1}^{k-1}|b_{p_i}-b_{p_{i+1}}| i=1kapi+i=1k1bpibpi+1

注意,读取由一条编号为 p 1 p_1 p1的报文组成的报文集所需的时间等于 a p 1 a_{p_1} ap1。此外,读取一组空信息的时间为 0 0 0

用户可以确定他愿意在APP上花费的时间 l l l。APP必须告知用户信息集的最大可能大小,其阅读时间不超过 l l l。请注意,信息集的最大大小可以等于 0 0 0

开发人员未能实现这一功能,帮助他们解决这一问题。

分析:

同样的元素集合,一定是按照 b b b升序排列花费最小。所以将 b b b进行升序排序,枚举左右两个端点, b b b的总和就确定了。取区间中尽量多的最小的 a a a,使得花费小于等于 l l l

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

void solve() {
    LL n, m;
    cin >> n >> m;
    vector<pair<LL, LL>> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i].first >> a[i].second;
    }
    sort(a.begin(), a.end(), [&](auto a, auto b) { return a.second < b.second; });
    LL ans = 0;
    for (int i = 1; i <= n; i++) {
        LL cnt = -a[i].second;
        priority_queue<int> q;
        for (int j = i; j <= n; j++) {
            q.push(a[j].first);
            cnt += a[j].first;
            if (!q.empty() && cnt + a[j].second > m) {
                cnt -= q.top();
                q.pop();
            }
            ans = max(ans, (LL) q.size());
        }
    }
    cout << ans << endl;
}

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

D.Exam in MAC(容斥)

题意:

硕士生中心公布了入学考试,考试内容如下。

给考生一个大小为 n n n的集合 s s s和一个整数 c c c。对于这个集合,计算出有多少对整数 ( x , y ) (x,y) (x,y)使得 0 ≤ x ≤ y ≤ c 0\leq x\leq y\leq c 0xyc,满足 x + y x+y x+y包含在集合 s s s中,以及 y − x y-x yx包含在集合 s s s中。

分析:

本题考虑容斥原理:

  • 总数 − ( x + y ) -(x+y) (x+y)属于 a [ i ] a[i] a[i]的方案数 − ( x − y ) -(x-y) (xy)属于 a [ i ] a[i] a[i]的方案数 + ( x + y ) +(x+y) +(x+y) ( x − y ) (x-y) (xy)属于 a [ i ] a[i] a[i]的方案数;
  • 对于 x + y = a [ i ] x+y=a[i] x+y=a[i]的个数就是 a [ i ] / 2 + 1 a[i]/2+1 a[i]/2+1
  • 对于 y − x = a [ i ] y-x=a[i] yx=a[i]的个数就是 c − a [ i ] + 1 c-a[i]+1 ca[i]+1的个数
  • 对于重复的,发现如果 x + y x+y x+y x − y x-y xy确定,则 x , y x,y x,y就确定了,而有解条件是 x + y x+y x+y x − y x-y xy同奇偶,那么把 s s s中的元素的奇数和偶数分别统计一下即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        LL n, c;
        cin >> n >> c;
        LL t1, t2, t3, t4;
        t1 = t2 = t3 = t4 = 0;
        for (LL i = 1; i <= n; i++) {
            int x;
            cin >> x;
            t1 += (x / 2) + 1;
            t2 += (c - x + 1);
            if (x & 1)
                t3++;
            else
                t4++;
        }
        c++;
        cout << c * (c - 1) / 2 + c - t1 - t2 + t3 * (t3 - 1) / 2 + t4 * (t4 - 1) / 2 + t3 + t4 << endl;
    }
    return 0;
}

E.Distance Learning Courses in MAC(前缀和)

题意:

研究生中心的新年已经到来,是时候推出一项新功能了!

现在,学生可以选修远程学习课程,共有 n n n门课程可供选择。对于第 i i i门远程学习课程,学生可以获得从 x i x_i xi y i y_i yi不等的成绩。

但是,并不是所有的课程每个学生都可以选修。具体来说,第 j j j名学生只能选修 l j l_j lj r j r_j rj的课程,即 l j , l j + 1 , … , r j l_j,l_j+1,\ldots,r_j lj,lj+1,,rj的远程学习课程。

远程学习课程的创建者决定用一种特殊的方式来确定最终成绩。让第 j j j名学生在他们的远程学习课程中获得 c l j , c l j + 1 , … , c r j c_{l_j},c_{l_j+1},\ldots,c_{r_j} clj,clj+1,,crj 的成绩。那么他们的最终成绩将等于 c l j c_{l_j} clj ∣ | c l j + 1 c_{l_j+1} clj+1 ∣ | … \ldots ∣ | c r j c_{r_j} crj ∣ | 表示按位或。

由于解决远程学习课程的聊天机器人坏了,对于 q q q个学生中的每一个,请你告诉他们可以达到的最高期末成绩。

分析:

首先考虑 x = 0 x=0 x=0。我们从最高有效位遍历到最低有效位,并尝试将它们包含在答案中。假设我们遍历第 i i i位,那么如果这样的一位在 y y y个数字中出现了 c c c次,就会出现几种情况:

  • c = 0 c=0 c=0 - 答案中不能包含该位
  • c = 1 c=1 c=1 - 该位将包含在答案中,我们将其加上
  • c > 1 c\gt 1 c>1 - 一种特殊情况,因为对于一个有位x的数字,我们可以设置它,而对于另一个数字,我们可以设置所有位 j < i j\lt i j<i

因此,如果我们遇到某个位 i i i出现不止一次,那么答案中也会包含所有位 j ≤ i j\le i ji

然后考虑原问题,我们取一对数字 ( x i , y i ) (x_i,y_i) (xi,yi),并找到按位最大的公共前缀,称之为数 w w w。显然, w w w的所有位都将包含在答案中,然后我们再建立一对新的数对 ( x i ′ , y i ′ ) (x^{'}_{i},y^{'}_{i}) (xi,yi)= ( x i − w , y i − w ) (x_i-w,y_i-w) (xiw,yiw),并记住数字 w i w_i wi。注意数字 y i ′ − 1 ≥ x i ′ y^{'}_{i}-1\ge x^{'}_{i} yi1xi。在某些位出现不止一次的情况下,我们会把它和所有更小的位加到答案中。为此,我们将在这个位置上设置一个等于 2 i − 1 2^i-1 2i1的数字(而其他较大的比特位则设置为 i i i)。但如果是 y i − 1 ′ ≥ x i ′ y^{'}_{i-1}\ge x^{'}_{i} yi1xi ,那么我们总是可以将所有这些位相加。

因此,求 j j j的求解算法如下:

  • 取所有 w i w_i wi的按位或结果( l j ≤ i ≤ r j l_j\le i\le r_j ljirj),设为数字 W W W
  • 对位 i i i进行遍历,与 x = 0 x=0 x=0的情况类似,对数组 y ′ y^{'} y进行遍历。同时,考虑到该位出现在 W W W中。

这种解法可以使用每个位的前缀和来实现。时间复杂度为 O ( n ⋅ log ⁡ n ) O(n\cdot\log n) O(nlogn)

代码:

#include <bits/stdc++.h>

using namespace std;
template<class T>
using vc = vector<T>;

const int N = 2e5;
const int bit = 30;

struct segtree {
    vc<int> t;
    int n;

    segtree(int n) : n(n) {
        t.resize(n * 2);
    }

    void upd(int i, int x) {
        for (t[i += n] = x; i > 1; i >>= 1) {
            t[i >> 1] = t[i] | t[i ^ 1];
        }
    }

    int get(int l, int r) {
        int res = 0;
        for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
            if (l & 1)
                res |= t[l++];
            if (r & 1)
                res |= t[--r];
        }
        return res;
    }
};

int n;
int l[N], r[N];
int w[N];

void fix() {
    for (int i = 0; i < n; ++i) {
        if (l[i] == r[i]) {
            w[i] = l[i];
            l[i] = r[i] = 0;
            continue;
        }
        int pref = (1 << (__lg(l[i] ^ r[i]) + 1)) - 1;
        w[i] = r[i] - (r[i] & pref);
        r[i] &= pref;
        l[i] &= pref;
    }
}

void solve() {
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> l[i] >> r[i];
    }
    fix();
    segtree t(n);
    vc<vc<int>> bits(bit, vc<int>(n + 1));
    for (int i = 0; i < n; ++i) {
        t.upd(i, w[i]);
        for (int j = 0; j < bit; ++j) {
            bits[j][i + 1] = bits[j][i];
            if (r[i] >> j & 1) {
                bits[j][i + 1]++;
            }
        }
    }
    int q;
    cin >> q;
    while (q--) {
        int x, y;
        cin >> x >> y;
        --x;
        int ans = t.get(x, y);
        for (int j = bit - 1; j >= 0; --j) {
            int cnt = bits[j][y] - bits[j][x] + (ans >> j & 1);
            if (cnt > 1) {
                ans |= (2 << j) - 1;
                break;
            } else if (cnt == 1) {
                ans |= 1 << j;
            }
        }
        cout << ans << ' ';
    }
    cout << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

赛后交流

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值