AtCoder Beginner Contest 380 A~E
A - 123233
洛谷链接
AtCoder链接
题目
给你一个
请判断
- 在
N 的数字中,数字1 恰好出现一次。 - 在
N 的数字中,数字2 恰好出现两次。 - 在
N 的数字中,数字3 恰好出现三次。
代码
#include <bits/stdc++.h>
using namespace std;
string s;
int a[3];
int main()
{
cin >> s;
for (int i = 0; i < s.length(); i ++ )
{
if (s[i] == '1')
a[0] ++;
else if (s[i] == '2')
a[1] ++;
else if (s[i] == '3')
a[2] ++;
}
if (a[0] == 1 && a[1] == 2 && a[2] == 3)
puts("Yes");
else
puts("No");
return 0;
}
B - Hurdle Parsing
洛谷链接
AtCoder链接
题目
伊洛哈有一个长度为
她用
- 以
|开始。 - 对于
i=1,2, \ldots ,N ,依次执行以下操作:- 将
A_i 个-加到S 的末尾。 - 然后,在
S 的末尾添加一份|。
- 将
在生成的字符串
代码
题意就是求每对 | 中 - 的个数。
#include <bits/stdc++.h>
using namespace std;
string s;
int cnt;
int main()
{
cin >> s;
for (int i = 1; i < s.length(); i ++ )
{
if (s[i] == '-')
cnt ++;
else
{
cout << cnt << ' ';
cnt = 0;
}
}
return 0;
}
C - Move Segment
洛谷链接
AtCoder链接
题目
给你一个长度为 0 和 1 组成。
将 1 字块移动到第 1 字块的后面,并输出得到的字符串。
保证 1 字块。
下面是更精确的描述。
- 让
S_{l \ldots r} 表示S 从l (第 3 个字符)到r (第 3 个字符)的子串。 - 如果
S 的子串S_{l \ldots r} 满足以下所有条件,我们就定义它为1字块: -
- 假设
S 中所有的1块都是S_{l_1 \ldots r_1}, \ldots ,S_{l_m \ldots r_m} ,其中l_1<l_2< \cdots <l_m 。
然后,我们定义长度为N 的字符串T ,将第K 个1字块移动到紧接着的第(K-1) 个1字块的位置,如下所示:- 当
1 \le i \le r_{K-1} 时,T_i=S_i 。 - 当
r_{K-1}+1 \le i \le r_{K-1}+(r_K-l_K)+1 时,T_i= 1。 - 当
r_{K-1}+(r_K-l_K)+2 \le i \le r_K 时,T_i= 0。 - 当
r_K+1 \le i \le N 时,T_i=S_i 。
- 当
思路
我们找到第
那么怎么找第
代码
可能会因无法输入 s + 1 而 CE,换一下 C++ 版本就好了。
#include <bits/stdc++.h>
using namespace std;
int n, k, qr, dl, dr, lx = 1, rx, op;
char s[500010];
int main()
{
cin >> n >> k >> s + 1;
for (int i = 1; i <= n && op <= k; i ++ )
{
if (s[i] == '1')
{
if (s[i-1] != '1')
op ++;
rx ++;
if (op == k - 1)
qr = rx;
if (op == k)
{
dl = lx;
dr = rx;
}
}
else if (s[i] == '0')
{
lx = i + 1;
rx = i;
}
}
for (int i = 1; i <= n; i ++ )
{
if (i == dl)
{
i = dr;
continue;
}
cout << s[i];
if (i == qr)
{
for (int j = dl; j <= dr; j ++ )
cout << 1;
}
}
return 0;
}
D - Strange Mirroring
洛谷链接
AtCoder链接
题目
有一个由大写和小写英文字母组成的字符串
我们对
- 首先,将
S 中的大写字母改为小写,将小写字母改为大写,从而创建一个字符串T 。 - 然后,依次连接
S 和T 形成新的S 。
回答
- 在完成所有运算后,找出
S 开头的第三个字符K_i 。
思路
我们可以发现,每次长度都会变成原来的两倍,那么如果我们对半砍,如果在一半的后面就给它对应到前一半的对应位置,记一次翻转,直到对应到
我们知道,这一个序列是同时变动的,所以我们可以把序列认为是一个字符,把
假设我们要找的位置时在四号三角形的那个序列里的,那么它一定三号三角形那个序列翻转后得到的,那么三号序列就是二号三角形那个序列翻转后得到的,直到第二个(一号)序列。这样是翻转了三次,加上它是第二个本来就翻转了一次,一共是四次,但是翻转四次和没翻转没有区别,所以就是原序列的那个位置,如果是奇数次翻转,那么就是原序列的那个位置转换一下大小写即可。
这时已经对上面的简洁的描述有了一定的理解,我们继续理解一下为什么是在一半的后面。如果是在五号序列的话,那么我们会直接到二号序列,在第二次对半砍的时候并不会有变化,那么如果我们仍然记了一次翻转,就会多翻转一次。
那么大部分需要理解的内容就结束了,那么怎么实现对半砍呢?我们可以发现每次对半砍完长度都是除以
那么我们现在解决了从多长开始砍,那么具体怎么砍呢?我们只要判断当前位置是否大于当前要砍的长度,如果不大于就行了,如果大于就减去这个长度,翻转次数加
最后判断一下翻转次数的奇偶性,翻一下即可。
代码
可能会因无法输入 s + 1 而 CE,换一下 C++ 版本就好了。
#include <bits/stdc++.h>
using namespace std;
char s[200010];
long long q, n, w[200010], cn, k, g, gk, t, a, c;
char bian(long long x)
{
if (x >= 'a' && x <= 'z')
return x - 'a' + 'A';
else
return x - 'A' + 'a';
}
int main()
{
cin >> s + 1 >> q;
n = strlen(s + 1);
while (q -- )
{
cn = 0;
scanf("%lld", &k);
g = (k + n - 1) / n;
gk = k % n;
if (gk == 0)
gk = n;
t = g;
a = 1;
while (t)
{
w[++ cn] = a;
t >>= 1;
a *= 2;
}
c = 0;
for (long long i = cn; i > 1; i -- )
{
if (g > w[i])
{
g -= w[i];
c ++;
}
}
if (g == 2)
c ++;
c %= 2;
if (c == 1)
cout << bian(s[gk]) << ' ';
else
cout << s[gk] << ' ';
}
return 0;
}
E - 1D Bucket Tool
洛谷链接
AtCoder链接
题目
一行中有
在每个
最初,
给您
1 x c:将以下单元格重新涂成颜色c :通过重复移动到与当前单元格相同颜色的相邻单元格,从单元格x 到达的所有可到达单元格。2 c:打印涂上颜色c 的单元格数量。
思路
用并查集维护几个信息:
其他再记录几个信息:
那么初始化都是很好做的。
为了方便,我们使一个点的正确左右端点存储在它的终极祖先(也就是
对于操作
操作
其次,我们把原来颜色的个数减少颜色块长度个,要改变到的颜色的个数加上颜色块长度个,把
然后我们判断一下这个颜色块和左右颜色是否变得相同了,如果颜色一样了,就合并到一起。
那么如何合并呢?首先,肯定要把一个的父亲赋成另一个,然后我们把那个在上面那个(终极祖先)的左右端点改变一下(左端点赋成两个左端点靠前的那个,右端点赋成两个右端点靠后的那个)。
代码
#include <bits/stdc++.h>
using namespace std;
int n, q, op, x, c, f[500010], cnt[500010], col[500010], l[500010], r[500010];
int find(int x)
{
if (x == f[x])
return x;
else
return f[x] = find(f[x]);
}
void merge(int x, int y)
{
x = find(x);
y = find(y);
f[x] = y;
l[y] = min(l[y], l[x]);
r[y] = max(r[y], r[x]);
}
int main()
{
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; i ++ )
{
f[i] = col[i] = l[i] = r[i]= i;
cnt[i] = 1;
}
while (q -- )
{
scanf("%d", &op);
if (op == 1)
{
scanf("%d %d", &x, &c);
x = find(x);
cnt[c] += r[x] - l[x] + 1;
cnt[col[x]] -= r[x] - l[x] + 1;
col[x] = c;
if (l[x] > 1 && c == col[find(l[x] - 1)])
merge(l[x]-1,x);
if (r[x] < n && c == col[find(r[x] + 1)])
merge(r[x] + 1, x);
}
else
{
scanf("%d", &c);
cout << cnt[c] << '\n';
}
}
return 0;
}