Codeforces Round 932(Div.2) A~E

A.Entertainment in MAC(字符串)

题意:

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

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

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

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

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

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

分析:

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

代码:

#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(区间)

题意:

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

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

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

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

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

分析:

如果有解,一定能只划分为两个区间。如果两个区间的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计划进行更新,开发人员希望在更新中优化显示给用户的信息集。目前共有nnn条信息。每条信息由两个整数aia_iaibib_ibi表示。阅读数字为p1,p2,…,pkp_1,p_2,\ldots,p_kp1,p2,,pk1≤pi≤n1\le p_i\le n1pin,所有pip_ipi都是不同的)的信息所花费的时间由公式计算得出:

∑i=1kapi+∑i=1k−1∣bpi−bpi+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

注意,读取由一条编号为p1p_1p1的报文组成的报文集所需的时间等于ap1a_{p_1}ap1。此外,读取一组空信息的时间为000

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

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

分析:

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

代码:

#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(容斥)

题意:

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

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

分析:

本题考虑容斥原理:

  • 总数−(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+1a[i]/2+1a[i]/2+1
  • 对于y−x=a[i]y-x=a[i]yx=a[i]的个数就是c−a[i]+1c-a[i]+1ca[i]+1的个数
  • 对于重复的,发现如果x+yx+yx+yx−yx-yxy确定,则x,yx,yx,y就确定了,而有解条件是x+yx+yx+yx−yx-yxy同奇偶,那么把sss中的元素的奇数和偶数分别统计一下即可。

代码:

#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(前缀和)

题意:

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

现在,学生可以选修远程学习课程,共有nnn门课程可供选择。对于第iii门远程学习课程,学生可以获得从xix_ixiyiy_iyi不等的成绩。

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

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

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

分析:

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

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

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

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

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

  • 取所有wiw_iwi的按位或结果(lj≤i≤rjl_j\le i\le r_jljirj),设为数字WWW
  • 对位iii进行遍历,与x=0x=0x=0的情况类似,对数组y′y^{'}y进行遍历。同时,考虑到该位出现在 WWW中。

这种解法可以使用每个位的前缀和来实现。时间复杂度为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、付费专栏及课程。

余额充值