Codeforces Round #738 (Div. 2) 题解
Posted 人形自走Bug生成器
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #738 (Div. 2) 题解相关的知识,希望对你有一定的参考价值。
旅行传送门
A. Mocha and Math
题意:给你一个序列,你可以任意次地进行如下操作:
选择一个任意的区间 \\([l,r]\\) ,对于区间内的所有值,用 \\(a_{l+i}\\) & \\(a_{r−i}\\) 替换 \\(a_{l+i}\\)
求序列最小化后的最大值。
题目分析:我们不妨考虑&的性质,两个数相与,只有在它们的二进制表示中该位均为1时,该位才会为1,即 \\(a\\) & \\(b\\) \\(\\leq\\) \\(a,b\\) ,因此我们的目标就是要让答案的二进制每一位尽可能为0,那么将整个序列都相与即为所求。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
int t, n, ans, x;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < \'0\' || ch > \'9\')
{
if (ch == \'-\')
f = -1;
ch = getchar();
}
while (ch >= \'0\' && ch <= \'9\')
{
x = x * 10 + ch - \'0\';
ch = getchar();
}
return x * f;
}
int main(int argc, char const *argv[])
{
t = read();
while (t--)
{
n = read();
ans = read();
rep(i, 2, n) x = read(), ans &= x;
printf("%d\\n", ans);
}
return 0;
}
B. Mocha and Red and Blue
题意:给你一个由"B、R、?"三种字符组成的字符串,将其中的?用B或R代替,相邻的字符会使不完美度+1,输出最小化不完美度时的字符串
题目分析:贪心地选择每个已经上色的字符,并使它相邻两侧的?涂上与它不同的颜色,然后再次选择新上色的字符,重复执行上述操作,我们可以利用队列来实现算法。最后再遍历一遍字符串,给仍未上色的字符周期性地用B、R代替。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < \'0\' || ch > \'9\')
{
if (ch == \'-\')
f = -1;
ch = getchar();
}
while (ch >= \'0\' && ch <= \'9\')
{
x = x * 10 + ch - \'0\';
ch = getchar();
}
return x * f;
}
//B is 1 , R is -1.
int main(int argc, char const *argv[])
{
int t = read();
while (t--)
{
std::queue<int> q;
int n = read();
char s[105];
int vis[105];
memset(vis, 0, sizeof(vis));
scanf("%s", s + 1);
rep(i, 1, n)
{
if (s[i] != \'?\')
{
vis[i] = (s[i] == \'B\' ? 1 : -1);
q.push(i);
}
}
while (!q.empty())
{
int pos = q.front();
q.pop();
if (vis[pos] == 1)
{
if (!vis[pos + 1] && pos + 1 <= n)
{
vis[pos + 1] = -1;
q.push(pos + 1);
}
if (!vis[pos - 1] && pos - 1 > 0)
{
vis[pos - 1] = -1;
q.push(pos - 1);
}
}
else if (vis[pos] == -1)
{
if (!vis[pos + 1] && pos + 1 <= n)
{
vis[pos + 1] = 1;
q.push(pos + 1);
}
if (!vis[pos - 1] && pos - 1 > 0)
{
vis[pos - 1] = 1;
q.push(pos - 1);
}
}
}
int a = 1, b = -1;
rep(i, 1, n) if (!vis[i]) vis[i] = a, std::swap(a, b);
rep(i, 1, n)
{
if (vis[i] == 1)
putchar(\'B\');
else if (vis[i] == -1)
putchar(\'R\');
}
puts("");
}
return 0;
}
C. Mocha and Hiking
题意:给你一张图,共有 \\(2n-1\\) 条边,其中 \\(n-1\\) 条边是从 \\(i\\) 至 \\(i-1\\) ,另外 \\(n\\) 条边是从 \\(i\\) 至 \\(n+1\\) 或从 \\(n+1\\) 至 \\(i\\),问是否存在一种方案使得可以遍历整张图。
题目分析:dfs会超时(血的教训),实际上无非就三种情况特判:
- 点 \\(n\\) 能否到达点 \\(n+1\\)
- 点 \\(n+1\\) 能到达点 \\(1\\)
- 是否存在某个点 \\(i\\) 可以到达点 \\(n+1\\) 并且从点 \\(n+1\\) 可以回到点 \\(i+1\\)
若三种情况都不满足,则不存在该方案
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
const int maxn = 1e4 + 5;
int a[maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < \'0\' || ch > \'9\')
{
if (ch == \'-\')
f = -1;
ch = getchar();
}
while (ch >= \'0\' && ch <= \'9\')
{
x = x * 10 + ch - \'0\';
ch = getchar();
}
return x * f;
}
int main(int argc, char const *argv[])
{
int t = read();
while (t--)
{
int n = read();
rep(i, 1, n) a[i] = read();
if (!a[n])
{
rep(i, 1, n + 1) printf("%d ", i);
puts("");
continue;
}
else if (a[1])
{
printf("%d ", n + 1);
rep(i, 1, n) printf("%d ", i);
puts("");
continue;
}
else
{
int pos = -1;
rep(i, 1, n) if (!a[i] && a[i + 1])
{
pos = i;
break;
}
if (pos != -1)
{
rep(i, 1, pos)
printf("%d ", i);
printf("%d ", n + 1);
rep(i, pos + 1, n)
printf("%d ", i);
puts("");
continue;
}
}
puts("-1");
}
return 0;
}
D1. Mocha and Diana (Easy Version)
题意:给你两张图,顶点数相同,初始边不同,在保证两张图是树形结构的情况下同时加边,问最多可以加多少条边,分别是哪些边。
题目分析:将已经连边的点放入同一个集合里,当我们要判断某两个点能否连边时,即看它们分别在两张图中是否都不属于同一个集合,因此可以用并查集维护,easy version \\(n\\) 的数据范围 \\(\\leq 1000\\) ,直接暴力枚举任意两点就好。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define pii pair<int, int>
using namespace std;
const int maxn = 1005;
int t, n, m1, m2, ans, cnt1, cnt2;
int fa1[maxn], fa2[maxn];
vector<pii> vec;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < \'0\' || ch > \'9\')
{
if (ch == \'-\')
f = -1;
ch = getchar();
}
while (ch >= \'0\' && ch <= \'9\')
{
x = x * 10 + ch - \'0\';
ch = getchar();
}
return x * f;
}
void init()
{
ans = cnt1 = cnt2 = 0;
rep(i, 0, n) fa1[i] = fa2[i] = i;
}
inline int find1(int x) { return x == fa1[x] ? x : fa1[x] = find1(fa1[x]); }
inline int find2(int x) { return x == fa2[x] ? x : fa2[x] = find2(fa2[x]); }
void merge1(int x, int y)
{
int fx = find1(x), fy = find1(y);
if (fx == fy)
return;
fa1[fx] = fy;
++cnt1;
}
void merge2(int x, int y)
{
int fx = find2(x), fy = find2(y);
if (fx == fy)
return;
fa2[fx] = fy;
++cnt2;
}
int main(int argc, char const *argv[])
{
n = read(), m1 = read(), m2 = read();
init();
int x, y;
rep(i, 1, m1)
{
x = read(), y = read();
merge1(x, y);
}
rep(i, 1, m2)
{
x = read(), y = read();
merge2(x, y);
}
rep(i, 1, n)
{
if (cnt1 == n - 1 || cnt2 == n - 1)
break;
rep(j, 1, n)
{
int fx1 = find1(i), fy1 = find1(j);
int fx2 = find2(i), fy2 = find2(j);
if ((fx1 != fy1) && (fx2 != fy2))
{
++ans;
merge1(i, j), merge2(i, j);
vec.push_back(make_pair(i, j));
}
}
}
printf("%d\\n", ans);
int nn = vec.size() - 1;
rep(i, 0, nn) printf("%d %d\\n", vec[i].first, vec[i].second);
return 0;
}
D2. Mocha and Diana (Hard Version)
题目分析:hard version \\(n\\) 的数据范围显然不允许我们使用 \\(n^2\\) 的算法,官方题解用的是启发式合并,其实评论区神犇给出了一种更优的解决方式:
首先任选一点 \\(i\\) ,尝试将 \\(i\\) 与其它所有点连边,设以点 \\(i\\) 为中心的大集合为 \\(U\\) ,此时会出现三种情况:
-
两张图中,均有 \\(j \\notin U\\) ,在点 \\(i\\) 与 点\\(j\\) 间加一条边,并将点 \\(j\\) 丢进 \\(U\\) 中
-
在第一、二张图中,分别有 \\(j \\notin U1\\) 与 \\(j \\in U2\\) ,我们将 \\(j\\) 记录下来并存入堆栈 \\(v1\\) 中
-
与上述情况相反,我们将 \\(j\\) 记录下来并存入堆栈 \\(v2\\) 中
接下来我们要做的就是匹配这些没有进入“大集合”中的点:
-
如果 \\(v1\\) 顶部的点在大集合 \\(U1\\) 中,则将其删除
-
如果 \\(v2\\) 顶部的点在大集合 \\(U2\\) 中,则将其删除
-
否则,在 \\(v1\\) 顶部的点和 \\(v2\\) 顶部的点之间添加一条边。
感性地理解下,就是先找到一个点,把它能连上的边全连了,然后再根据两张图的情况在剩下的点间连边
可以证明该算法的正确性,且其复杂度近乎为 \\(O(n+m)\\)
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define pii std::pair<int, int>
#define mp std::make_pair
const int maxn = 1e5 + 10;
int n, m1, m2, x, y, fa1[maxn], fa2[maxn];
std::vector<int> v1, v2;
std::vector<pii> ans;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < \'0\' || ch > \'9\')
{
if (ch == \'-\')
f = -1;
ch = getchar();
}
while (ch >= \'0\' && ch <= \'9\')
{
x = x * 10 + ch - \'0\';
ch = getchar();
}
return x * f;
}
inline int find1(int x) { return x == fa1[x] ? x : fa1[x] = find1(fa1[x]); }
inline int find2(int x) { return x == fa2[x] ? x : fa2[x] = find2(fa2[x]); }
void merge1(int x, int y)
{
int fx = find1(x), fy = find1(y);
if (fx == fy)
return;
fa1[fx] = fy;
}
void merge2(int x, int y)
{
int fx = find2(x), fy = find2(y);
if (fx == fy)
return;
fa2[fx] = fy;
}
int main(int argc, char const *argv[])
{
n = read(), m1 = read(), m2 = read();
rep(i, 1, n) fa1[i] = fa2[i] = i;
rep(i, 1, m1)
{
x = read(), y = read();
merge1(x, y);
}
rep(i, 1, m2)
{
x = read(), y = read();
merge2(x, y);
}
rep(i, 2, n)
{
int fx1 = find1(1), fy1 = find1(i);
int fx2 = find2(1), fy2 = find2(i);
if (fx1 ^ fy1 && fx2 ^ fy2)
{
merge1(1, i), merge2(1, i);
ans.push_back(mp(1, i));
}
if (fx1 ^ fy1)
v1.push_back(i);
if (fx2 ^ fy2)
v2.push_back(i);
}
while (v1.size() && v2.size())
{
x = v1.back(), y = v2.back();
int f1 = find1(1), f2 = find2(1);
int fx = find1(x), fy = find2(y);
if (fx == f1)
{
v1.pop_back();
continue;
}
if (fy == f2)
{
v2.pop_back();
continue;
}
merge1(x, y), merge2(x, y);
ans.push_back(mp(x, y));
}
printf("%d\\n", ans.size());
while (ans.size())
{
printf("%d %d\\n", ans.back().first, ans.back().second);
ans.pop_back();
}
return 0;
}
以上是关于Codeforces Round #738 (Div. 2) 题解的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #738 (Div. 2) 题解
Codeforces Round #738 (Div. 2) A - D1 题解
Codeforces Round #738 (Div. 2) A - D1 题解
Codeforces Round #738 (Div. 2) A - D1 题解