AtCoder Beginner Contest 380 Solution

A - 123233

6个数问是不是1个1,2个2,3个3

#include <bits/stdc++.h>
using namespace std;
int a[4];
int main()
{
    string s;
    cin >> s;
    for (int i = 0; i < s.size(); i++)
        a[s[i] - '0']++;
    if (a[1] == 1 && a[2] == 2 && a[3] == 3)
        cout << "Yes";
    else
        cout << "No";
    return 0;
}

B - Hurdle Parsing

统计每两个|之间有多少-。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin >> s;
    vector<int> a;
    int cnt = 0;
    for (int i = 1;i < s.size();i++) {
        if (s[i] == '|') {
            a.push_back(cnt);
            cnt = 0;
        }
        else
            cnt++;
    }
    for (int i = 0;i < a.size();i++)
        cout << a[i] << " ";
    return 0;
}

C - Move Segment

题意

给定一个01子串。

连续的1视为一个块。

现将第k个块移动到第k−1个块前面。

解法

把每一段的长度以及是0是1存进一个vector里,同时记录第k个块在vector里的下标,然后swap一下他和前一段即可。

#include<bits/stdc++.h>
using namespace std;
int main() {
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    int idx = 0, id;
    vector<pair<int, char> > p;//存长度,是0是1。
    for (int i = 0; i < n; i++) {
        int j = i;
        while (j + 1 < n && s[j + 1] == s[j]) j++;
        if (s[i] == '1') {
            ++idx;
            if (idx == k)//记录第k个全1段在p中的下标
                id = p.size();
        }
        p.push_back({ j - i + 1, s[i] });
        i = j;
    }
    if (id)
        swap(p[id], p[id - 1]);
    for (int i = 0;i < p.size();i++) {
        cout << string(p[i].first, p[i].second);
    }
    return 0;
}

D - Strange Mirroring

题意

给定一个字符串s,重复下述操作无数次:

  • 将s的字母大小写反转成t,加到s后面

给定q个询问,每个询问问第k个字符是什么。

思路

最后整个字符串就是由无数s和s的翻转构成,我们设s的翻转叫做s',那么我们现在考虑第i个段是s还是s‘。

举例现在求第30个字符串是原字符串还是翻转字符串:

一定在某次操作中,将第1-16个字符串翻转并复制到了17-32个字符串,所以可以求出第30个字符串是第14个字符串的翻转。

同理,在某次操作中,将1-8个字符串翻转到了9-16,第14个字符串就是第6个字符串的翻转。

以此类推,我们可以求出第30个字符串是原始字符串的几次翻转,求的次数是log次。

所以写程序就是模仿我们这个过程,每次找出是哪一次操作复制到的我们当前位置的字符串,然后找到对应的位置继续求即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll pow2[70];
int main() {
    string s;
    cin >> s;
    pow2[0] = 1;
    for (int i = 1;pow2[i - 1] <= 1e18;i++)
        pow2[i] = pow2[i - 1] * 2;//预处理2的次幂,即每次操作的翻倍点
    int Q;
    cin >> Q;
    while (Q--) {
        ll K;
        cin >> K;
        K--;
        ll k = (K / (int)s.size()) + 1;
        K %= (int)s.size();
        bool flag = 1;
        while (k != 1) {
            int id = lower_bound(pow2, pow2 + 61, k) - pow2-1;//找出当前下标是由哪一段翻倍
            k = k - pow2[id];//还原翻倍点
            flag ^= 1;//取反 表示最后是翻转的还是非翻转
        }
        if (flag) cout << s[K] << " ";
        else {
            if (isupper(s[K]))
                cout << (char)tolower(s[K]) << " ";
            else
                cout << (char)toupper(s[K]) << " ";
        }
    }
    return 0;
}

E - 1D Bucket Tool

题意

从左到右n个格子,第i个格子颜色为i 。

维护q个查询,分两种:

  • 1 x c:将第x个格子连同周围与其同色的格子涂成颜色c
  • 2 c:问颜色c的格子数

解法

用一个set维护每一段相同颜色区间的左端点以及颜色,经过一次修改之后,看新的颜色和前一段或者后一段是否相同,如果是相同颜色那么就可以合并,顺便记录一个数组维护每个颜色的格子数就行。

#include<bits/stdc++.h>
using namespace std;
int cnt[500005];//记录每个颜色的格子数
int main() {
    set<pair<int, int> > s;//左端点,颜色
    int n, Q;
    cin >> n >> Q;
    for (int i = 1;i <= n;i++) {
        s.insert({ i,i });
        cnt[i] = 1;
    }
    while (Q--) {
        int op;
        cin >> op;
        if (op == 1) {
            int x, c;
            cin >> x >> c;
            auto it = s.upper_bound({ x,1e9 });
            it--;//找到x位置是在哪一段
            if (it->second == c) continue;
            int ed;
            int st = it->first;//当前段左端点
            if (next(it) == s.end()) ed = n;//通过下一段的左端点计算当前段的右端点
            else ed = next(it)->first - 1;
            cnt[it->second] -= (ed - st + 1);//更新格子数
            cnt[c] += (ed - st + 1);
            vector<pair<int, int> > v;
            if (next(it) != s.end() && next(it)->second == c)//看下一段是不是颜色相同
                v.push_back(*next(it));
            if (it != s.begin() && prev(it)->second == c) {//上一段
                st = prev(it)->first;
                v.push_back(*prev(it));
            }
            s.erase(it);
            for (int i = 0;i < v.size();i++)//把颜色相同的段删掉
                s.erase(v[i]);
            s.insert({ st,c });//只保留合并的新段
        }
        else {
            int c;
            cin >> c;
            cout << cnt[c] << endl;
        }
    }
    return 0;
}
posted @ 2024-11-30 23:32  ~清渠~  阅读(7)  评论(0编辑  收藏  举报