Codeforces Pinely Round 2 (Div. 1 + Div. 2)

文章讲述了三道编程竞赛题目,涉及在线用户动态分析(A.Channel),通过消息操作判断用户阅读状态,以及如何通过最少操作次数使数组递增(B.SplitSort)。还讨论了MEXRepetition问题,即在给定数组中重复应用MEX操作求解最终序列。
摘要由CSDN通过智能技术生成

目录

A. Channel

AC代码:

B. Split Sort

AC代码:

C. MEX Repetition

AC代码:


比赛链接:

Dashboard - Pinely Round 2 (Div. 1 + Div. 2) - Codeforces

A. Channel

题目大意:Petya知道总共有n个人订阅了他的频道,起初有a个人在线(我们规定只要Petya的订阅者在线,就读过Petya的帖子),Petya收到了q个消息,每个消息用'+'代表随机一个订阅者上线,用'-'代表随机一个订阅者下线,根据这q个消息判断是以下三种情况中的哪一种

  • 这n个用户不可能都阅读了帖子
  • 有可能这n个用户都阅读了帖子
  • 这n个用户保证都阅读了帖子

思路:可以先将a个在线的人编入这q个消息中进行判断,当有n个连续的'+'或者经'-'抵消掉部分'+'后的连续'+'数目 >= n时可以保证这n个人都阅读了帖子。我们用cnt来记录这q+a个消息中'+'的出现次数,若cnt >= n则代表可能这n个用户阅读了帖子,否则这n个用户不可能都阅读了帖子

AC代码:

typedef vector<int> vi;
#define FOR(i, a, b) for (int i = a; i < (b); i++)
#define sz(x) (int)(x).size()

void solve() {
    int i, j, k, n, a, q;
    cin >> n >> a >> q;
    string s;
    cin >> s;
    while (a--) s = "+" + s;
    int res, cnt;
    res = cnt = 0;
    bool flag = false;
    FOR(i, 0, sz(s)) {
        int count = 0;
        if (s[i] == '+') {
            while (s[i] == '+') {
                res++;
                count++;
                cnt++;
                i++;
            }
            i--;            
        } else res--;
        if (count == n || res >= n) flag = true;
    }
    if (flag) cout << "YES" << endl;
    else if (cnt >= n) cout << "MAYBE" << endl;
    else cout << "NO" << endl;
}

B. Split Sort

题目大意:给你个长度为n的排列p,你可以执行一种操作:选择一个x(2 <= x <= n),令排列中所有比x小的数均按所给排列顺序不变的写在x的前面,所有比x大的数(包括x)均按所给排列的顺序不变的接上前者,然后将此排列替换为原先的排列。问若要使排列递增序,最小的操作次数是多少

思路:通过观察不难发现,如果要使排列递增序,那么需要选择排列中所有没有按排列递增序的数进行操作,也就是将此排列分成n个段,每个段严格遵守a[i] + 1 = a[i + 1],因为每个排列必然会存在一个段为数字1开头,所以这一段根本不用进行交换操作,所以最终的答案即为这n - 1。

以样例5为例:6 4 3 5 2 1

我们可以分为8个段

1 2 3 

4

5 6

7 8 9

10 11 12 13

14 15 16

17 18

19

因为1 2 3的段本身已经按排列递增排序,所以我们只需要分别选择数字:4,5,7,10,14,17,19这七位数即可完成最少操作次数

实现上可以利用set,如果set中含有当前数减一的数就进行替换,否则就插入set中,最终的set.size() - 1即为最小操作次数

AC代码:

typedef vector<int> vi;
#define FOR(i, a, b) for (int i = a; i < (b); i++)
#define sz(x) (int)(x).size()
#define lb lower_bound

void solve() {
    int i, j, k, n;
    cin >> n;
    vi nums(n, 0);
    set<int>st;
    FOR(i, 0, n) {
        cin >> nums[i];
        if (st.count(nums[i] - 1)) {
            st.erase(st.lb(nums[i] - 1));
            st.insert(nums[i]);
        } else st.insert(nums[i]);
    }
    cout << sz(st) - 1 << endl;
}

C. MEX Repetition

题目大意:给你长度为n的数组,数组中均为范围为0 ~ n的两两不相同的整数,可以进行一个操作:令数组中的每个下标i(1 <= i <= n)依次替换为MEX(a1,a2,…,an)。其中MEX(a1,a2,…,an)为数组中未出现的最小的非负整数,求进行k次这样的操作后的数组并打印

思路:总共出现了0 ~ n中的所有数,我们可以先遍历一遍数组看哪个数没出现过,然后将这个数加入到这个数组的头部,以样例0,1 ,3进行举例

遍历数组未出现2,将其加入数组头即为2,0,1,3,通过枚举不难看出,k = 1时数组变为2,0,1,k = 2时数组变为3,2,0,k = 3时数组变为1,3,2,k = 4时数组变为0,1,3,k = 5时数组变为2,0,1,这就发现一个规律,答案是进行循环的,所以我们可以对k进行适当取模即可进行模拟了

AC代码:

typedef vector<int> vi;
#define FOR(i, a, b) for (int i = a; i < (b); i++)
#define sz(x) (int)(x).size()

void solve() {
    int i, j, k, n;
    cin >> n >> k;
    vi nums(n + 1, 0);
    map<int, int>mp;
    set<int>st;
    FOR(i, 1, n + 1) {
        int x;
        cin >> x;
        nums[i] = x;
        mp[x] = i;
        st.insert(x);
    }
    FOR(i, 0, n + 1) {
        if (!st.count(i)) nums[0] = i, mp[i] = 0;
    }
    vi item(nums);
    reverse(item.begin() + 1, item.end());
    int cnt = k % sz(nums);
    if (cnt == 0) cnt = n;
    else cnt--;
    cnt = mp[item[cnt]];
    while (n--) {
        cout << nums[cnt] << " ";
        cnt++;
        if (cnt == sz(nums)) cnt = 0;
    }
    cout << endl;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值