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个格子连同周围与其同色的格子涂成颜色c2 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;
}