AtCoder Grand Contest 035
Contest Info
[Practice Link](https://atcoder.jp/contests/agc035)
Solved | A | B | C | D | E | F |
---|---|---|---|---|---|---|
3/6 | O | O | Ø | - | - | - |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A.XOR Circle
题意:
有\(n\)个数,形成一个环,问能否有一种排列使得换上的每个数都等于它相邻两个数的异或
思路:
考虑几种情况:
- 全0是可以的,并且长度没有限制
- 110110这种也是可以的,也就是只有两类数\(a, b\),并且\(a\)的个数是\(b\)的两倍,而且\(b\)移动是\(0\),而且长度要满足是\(3\)的倍数
- 123123这种也是可以的,也就是有两类数\(a, b, c\),并且\(a \oplus b = c\),并且三类数的个数相等,长度满足是\(3\)的倍数
代码:
#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define pii pair <int, int>
#define fi first
#define se second
int n, a[N];
map <int, int> mp;
int main() {
while (scanf("%d", &n) != EOF) {
mp.clear();
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
++mp[a[i]];
}
vector <pii> vec;
for (auto it : mp) vec.push_back(it);
int sze = mp.size();
if (sze == 1) {
if (vec[0].fi == 0) {
puts("Yes");
} else {
puts("No");
}
} else {
if (n % 3) {
puts("No");
} else {
if (sze == 2) {
if ((vec[0].se == vec[1].se * 2 && vec[1].fi == 0) || (vec[0].se * 2 == vec[1].se && vec[0].fi == 0)) {
puts("Yes");
} else {
puts("No");
}
} else if (sze == 3) {
if (vec[0].se == vec[1].se && vec[0].se == vec[2].se && ((vec[0].fi ^ vec[1].fi) == vec[2].fi)) {
puts("Yes");
} else {
puts("No");
}
} else {
puts("No");
}
}
}
}
return 0;
}
B. Even Degrees
题意:
有\(n\)个点,\(m\)条边的无向简单图,问能否给每条边定向使得每个点的出度都是偶数。
思路:
我们先考虑随意给边定向,然后考虑需要改变的是出度为奇数的点。
我们考虑找两个出度为奇数的点,然后改变这两个点一条无向连通路径上的所有边的指向,我们发现最后只有这两个点的出度的奇偶性发生变换,中间的点的出度的奇偶性都不变。
那么结论就出来了,只有当最后还剩下偶数个出度为奇数的点的时候,那么是可以做到的。
两两配对改变状态即可,然后跑一棵\(DFS\)树或者\(BFS\)树,在树上标记路径状态翻转即可。
那为什么刚开始随机给边定向是可以的?
我们先随机被边定向,然后从这个图的状态改变到另一个图的状态,可以通过翻转这个图中某条边的指向来达到。
可以归纳为两种情况:
- 翻转的边相邻的两点的出度奇偶性相同,那么翻转之后出度会变成全偶或者全奇,即总的出度为偶数的点的个数要么\(+2\),要么\(-2\),不影响奇偶性
- 翻转的两边相邻的两点的出度的奇偶性为一奇一偶,那么翻转之后还是一奇一偶。
代码:
#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define pii pair <int, int>
#define fi first
#define se second
int n, m;
int e[N][2];
vector <vector<pii>> G;
int used[N], d[N], del[N], vis[N];
int fa[N], deep[N], sze[N], son[N], top[N];
void DFS(int u) {
used[u] = 1;
sze[u] = 1;
for (auto it : G[u]) {
int v = it.fi;
int id = it.se;
if (v == fa[u]) continue;
if (used[v]) del[id] = 1;
if (!used[v]) {
if (e[id][0] != u) {
e[id][0] = u;
e[id][1] = v;
d[u] ^= 1;
d[v] ^= 1;
}
fa[v] = u;
deep[v] = deep[u] + 1;
e[id][0] = u;
e[id][1] = v;
DFS(v);
sze[u] += sze[v];
if (!son[u] || sze[v] > sze[son[u]]) son[u] = v;
}
}
}
void gettop(int u, int tp) {
top[u] = tp;
if (!son[u]) return;
gettop(son[u], tp);
for (auto it : G[u]) if (!del[it.se]) {
int v = it.fi;
if (v != fa[u] && v != son[u]) {
gettop(v, v);
}
}
}
int query(int u, int v) {
while (top[u] != top[v]) {
if (deep[top[u]] < deep[top[v]]) swap(u, v);
u = fa[top[u]];
}
if (deep[u] > deep[v]) swap(u, v);
return u;
}
void DFS2(int u) {
for (auto it : G[u]) if (!del[it.se]) {
int v = it.fi;
if (v != fa[u]) {
DFS2(v);
vis[u] += vis[v];
}
}
}
void DFS3(int u) {
for (auto it : G[u]) if (!del[it.se]) {
int v = it.fi;
int id = it.se;
if (v != fa[u]) {
if (vis[v] % 2) {
e[id][0] = v;
e[id][1] = u;
}
DFS3(v);
}
}
}
int main() {
while (scanf("%d%d", &n, &m) != EOF) {
G.clear(); G.resize(n + 1);
memset(used, 0, sizeof used);
memset(d, 0, sizeof d);
memset(son, 0, sizeof son);
memset(del, 0, sizeof del);
memset(vis, 0, sizeof vis);
for (int i = 1; i <= m; ++i) {
int &u = e[i][0], &v = e[i][1];
scanf("%d%d", &u, &v);
d[u] ^= 1;
G[u].push_back(pii(v, i));
G[v].push_back(pii(u, i));
}
DFS(1); gettop(1, 1);
vector <int> vec;
for (int i = 1; i <= n; ++i) if (d[i]) {
vec.push_back(i);
}
if (vec.size() % 2 == 1) {
puts("-1");
} else {
while (!vec.empty()) {
int u = vec.back(); vec.pop_back();
int v = vec.back(); vec.pop_back();
int lca = query(u, v);
++vis[u]; ++vis[v];
vis[lca] -= 2;
}
DFS2(1);
DFS3(1);
for (int i = 1; i <= m; ++i) printf("%d %d\n", e[i][0], e[i][1]);
}
}
return 0;
}
C. Skolem XOR Tree
题意:
有一棵\(2n\)个点的树,第\(i\)个点的权值和第\(n + i\)个点的权值都是\(i\),要求构造出一棵树,使得\(i \rightarrow (n + i)\)简单路径上的权值异或和为\(i\)。
思路:
- 如果\(n\)是\(2\)的幂次,那么无解。因为小于\(n\)的数不管怎么异或都不会异或成\(n\)
- 否则一定有解,可以考虑这么构造:
-
奇数的时候:
-
偶数的时候:
我们发现一定会存在三个数\(x, y, 1(1 < x, y < n)\)异或起来等于\(n\),那么我们按上述方式构造的时候,强制让\(i\)与\(n + 1\)相连,这样就方便找到\(x, y\)然后进行连边。
-
代码:
#include <bits/stdc++.h>
using namespace std;
#define N 100010
int n;
void P(int x, int y) {
printf("%d %d\n", x, y);
}
int main() {
while (scanf("%d", &n) != EOF) {
int m = 1;
while (m < n) m <<= 1;
if (n < 3 || m == n) puts("No");
else {
puts("Yes");
P(1, n + 2); P(n + 2, 3);
P(3, n + 1); P(n + 1, 2);
P(2, n + 3);
for (int i = 4; i < n; i += 2) {
P(i, n + 1); P(i, n + i + 1);
P(i + 1, n + 1); P(n + i, i + 1);
}
if (n % 2 == 0) {
for (int i = 2; i < n; ++i) {
if ((n ^ (i ^ 1)) < n) {
int a = i;
int b = n ^ i ^ 1;
P(n, a); P(n + n, b);
break;
}
}
}
}
}
return 0;
}