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;
}
posted @ 2019-07-15 07:44  Dup4  阅读(386)  评论(0编辑  收藏  举报