Codeforces Round #628 (Div. 2) 题解

Codeforces Round #628 (Div. 2)

A. EhAb AnD gCd

题意:构造一组 \((a,b)\) 满足 \(GCD(a,b)+LCM(a,b)=x\)

分析\(a=1,b=x-1\)

#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma comment(linker, "/stack:200000000")
#include <bits/stdc++.h>
#define SIZE 200010
#define rep(i, a, b) for (long long i = a; i <= b; ++i)
#define mp make_pair
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
ll n, t;

int main() {
	io(); cin >> t;
	rep(ii, 1, t) {
		cin >> n;
		cout << 1 << ' ' << n - 1 << '\n';
	}
}

B. CopyCopyCopyCopyCopy

题意:给定一个序列 \(a\) ,并将这个序列复制无数份接在一起,求这个无限长序列中的最长上升子序列。

分析:由于复制了无限次,答案就是不同种类数字的数量。

#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma comment(linker, "/stack:200000000")
#include <bits/stdc++.h>
#define SIZE 1000010
#define rep(i, a, b) for (long long i = a; i <= b; ++i)
#define mp make_pair
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
int n, t, ans;
 
int main() {
	io(); cin >> t;
	rep(ii, 1, t) {
		cin >> n;
		vector<int> a(n);
		rep(i, 0, (n - 1)) cin >> a[i];
		sort(a.begin(), a.end());
		a.erase(unique(a.begin(), a.end()), a.end());
		cout << a.size() << '\n';
	}
}

C. Ehab and Path-etic MEXs

题意:给定一棵 \(n\) 个点的树,你可以对树边标号为 \(0,1,2,...,n-2\) 。定义 \(MEX(u,v)\) 是说点 \(u\)\(v\) 的路径上最小的未出现的标号(例如路径上的标号是 \(0,2,3\) , 那结果就是 \(1\) ),给出一种构造方案使得对于所有 \(MEX(u,v)\) 的最大值最小。

分析:如果至少有一个点的度数 \(\geq3\) (即不为链),那么我们就能把 \(0,1,2\) 这三个数字分配到该点的三条邻边上,这样就能保证我任选一条路径不能同时存在这三个最小的数。

#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma comment(linker, "/stack:200000000")
#include <bits/stdc++.h>
#define SIZE 200010
#define rep(i, a, b) for (long long i = a; i <= b; ++i)
#define mp make_pair
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
int n, t, cnt;
int deg[SIZE];
vector<int> vec[SIZE];
struct Node {
	int x, y;
	int id;
	int val;
	Node() {}
	Node(int x_, int y_) : x(x_), y(y_) {}
	bool operator < (const Node& b) const {
		return (x == b.x ? y < b.y : x < b.x);
	}
}p[SIZE];
map<Node, int> MP;
 
int main() {
	io(); cin >> n;
	rep(i, 2, n) {
		int x, y; cin >> x >> y;
		if (x > y) swap(x, y);
		vec[x].emplace_back(y);
		vec[y].emplace_back(x);
		MP[Node(x, y)] = -1;
		p[i - 1] = Node(x, y); p[i - 1].id = i;
		++deg[x], ++deg[y];
	}
	bool f = false;
	rep(i, 1, n) {
		if (deg[i] > 2) {
			for (auto it : vec[i]) {
				int x = i, y = it;
				if (x > y) swap(x, y);
				MP[Node(x, y)] = cnt++;
			}
			break;
		}
	}
	rep(i, 1, (n - 1)) {
		if (MP[Node(p[i].x, p[i].y)] != -1) cout << MP[Node(p[i].x, p[i].y)] << '\n';
		else cout << cnt++ << '\n';
	}
}

D. Ehab the Xorcist

题意:给定两个数字 \(u\)\(v\) ,构造一个最短的数列 \(a_n\) 满足 \(a_1\oplus a_2\oplus ... \oplus a_n=u\) 并且 \(\sum^n_{i=1}a_i=v\)

分析:首先考虑不能构造的情况,容易发现 \(u>v\) 是不能构造的,然后根据奇偶性可以发现 \(v-u\) 为奇数时不能构造。在排除了本题的一些特判,比如 \(u=v=0\)\(u=v\) 之后,本题就转化为 \(v-u\) 为偶数时的构造问题。由于 \(v-u\) 为偶数,我们很快就能给出构造的上界是 \(3\) ,因为 (v - u) / 2 ^ (v - u) / 2 ^ u = u 。但是还要特判一下能否构造长度为 \(2\) 的数列,即 (v - u) / 2 ^ ((v - u) / 2 ^ u) = u

#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma comment(linker, "/stack:200000000")
#include <bits/stdc++.h>
#define SIZE 200010
#define rep(i, a, b) for (long long i = a; i <= b; ++i)
#define mp make_pair
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
ll n, m;

int main() {
	io(); cin >> n >> m;
	if (n > m) cout << -1;
	else if (!n && !m) cout << 0;
	else if (n == m) cout << 1 << '\n' << m;
	else {
		if ((m - n) % 2 == 0) {
			ll x = n, y = (m - n) / 2;
			ll z = (x ^ y);
			if (z + y == m) cout << "2\n" << z << ' ' << y;
			else cout << "3\n" << x << ' ' << y << ' ' << y;
		}
		else cout << -1;
	}
}

E. Ehab's REAL Number Theory Problem

题意:给定一个数组 \(a_n\) ,数组中任意一个元素的因子数不超过 \(7\) ,找出一个最短的子序列,满足该子序列之积为完全平方数。

分析:由于任意元素因子数不超过 \(7\) ,因此 \(a_i\) 的质因子数 \(\leq 2\) (如果质因子数为 \(3\) 将有 \(8\) 个因子)。因此我们先将所有给定元素进行预处理,去除所有平方因子(假设一个元素为 \(2^2\cdot 3\) 那它等价于 \(3\) ),然后所有元素只能是 \(1,p,pq\) 中的一种形式( \(p,q\) 为质数)。完成预处理后不难发现,如果存在 \(1\) ,那么结果就是最短子序列长度就是 \(1\) ;如果存在两个相同元素,那么结果就是 \(2\)

然后,我们就可以按照处理后的元素建图了,假设一个元素是 \(pq\) ,那么在 \(p\)\(q\) 之间建一条边;假设一个元素是 \(p\) 那么在 \(p\)\(1\) 之间建边;本题的答案就是该图上的最小环,因为每个点都会被两条边连接,说明每个点被选中了偶数次。由于边权全部为 \(1\) ,直接跑 \(bfs\) 即可,但是如果对每个节点搜索,复杂度是 \(O(NM)\) 的,本题中我们只需要对所有权值 \(\leq \sqrt{max\ {a_i}}\) 的节点搜索,因为权值 \(> \sqrt{max\ {a_i}}\) 的节点只可能出现 \(1\) 次,必定为奇数不能成环。

#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma comment(linker, "/stack:200000000")
#include <bits/stdc++.h>
#define SIZE 1000010
#define rep(i, a, b) for (long long i = a; i <= b; ++i)
#define mp make_pair
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
int n, ans = 2e9;
vector<int> a, fac;
vector<int> vec[SIZE];
int dis[SIZE];
bool vis[SIZE];

int init(int x) {
	for (int i = 2; i * i <= x; ++i) {
		if (x % i) continue;
		int cnt = 0;
		while (x % i == 0) x /= i, ++cnt;
		if (cnt & 1) x *= i;
	}
	return x;
}

void bfs(int x) {
	for (auto i : fac) dis[i] = 2e9, vis[i] = 0;
	dis[x] = 0;
	queue<int> q;
	q.push(x);
	vis[x] = true;
	while (!q.empty()) {
		int top = q.front();
		q.pop();
		vis[top] = false;
		for (auto i : vec[top]) {
			if (dis[i] > dis[top] + 1) {
				dis[i] = dis[top] + 1;
				q.push(i);
				vis[i] = true;
			}
			else if (vis[i]) ans = min(ans, dis[top] + dis[i] + 1);
		}
	}
}

int main() {
	io(); cin >> n;
	a.resize(n);
	rep(i, 0, (n - 1)) {
		cin >> a[i];
		a[i] = init(a[i]);
	}
	sort(a.begin(), a.end());
	if (a[0] == 1) { cout << 1; return 0; }
	a.erase(unique(a.begin(), a.end()), a.end());
	if (a.size() < n) { cout << 2; return 0; }

	for (auto i : a) {
		vector<int> tmp(2, -1);
		tmp[0] = i;
		for (int j = 2; j * j <= i; ++j) {
			if (i % j) continue;
			tmp[0] = j;
			i /= j;
			if (i != 1) tmp[1] = i;
			break;
		}
		if (tmp[1] == -1) tmp[1] = 1;
		for (auto j : tmp) fac.emplace_back(j);
		vec[tmp[0]].emplace_back(tmp[1]);
		vec[tmp[1]].emplace_back(tmp[0]);
	}
	sort(fac.begin(), fac.end());
	fac.erase(unique(fac.begin(), fac.end()), fac.end());
	rep(i, 1, 1000) {
		if (vec[i].empty()) continue;
		bfs(i);
	}
	cout << (ans == 2e9 ? -1 : ans);
}

F. Ehab's Last Theorem

题意:给定一个 \(n\) 个点 \(m\) 条边的无向图。要求找出一个点数恰好为 \(\lceil \sqrt{n} \rceil\) 的独立集,或者找出一个点数 \(\geq\lceil \sqrt{n} \rceil\) 的环。

分析:和 \(CF1103C\) 有异曲同工之妙,这类题型都可以通过 \(dfs\) 树给出构造。首先,找环是很容易的,直接在 \(dfs\) 树上找返祖边(回边)即可,如果存在点数 \(\geq\lceil \sqrt{n} \rceil\) 的环就直接输出。然后再考虑独立集的构造,我们用 \(\lceil \sqrt{n} \rceil-1\) 种颜色,给 \(dfs\) 树上的节点按照 \(dep[i]\%(\lceil \sqrt{n} \rceil-1)\) 的规则染色,由于 \(dfs\) 树的特性,相同深度的节点必定构成一个独立集,而在模意义下相同的任意两个不同深度节点之间,如果存在直连边就说明存在一个节点数 \(\geq\lceil \sqrt{n} \rceil\) 的环。因此我们只需要找到一个点数 \(\geq\lceil \sqrt{n} \rceil\) 的集合并恰好输出 \(\lceil \sqrt{n} \rceil\) 个点。

#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma comment(linker, "/stack:200000000")
#include <bits/stdc++.h>
#define SIZE 200010
#define rep(i, a, b) for (long long i = a; i <= b; ++i)
#define mp make_pair
#define ll long long
using namespace std;
void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
int n, m, lim;
int pa[SIZE], dep[SIZE], num[SIZE];
vector<int> vec[SIZE];

void dfs(int now) {
	++num[dep[now] % (lim - 1)];
	for (auto to : vec[now]) {
		if (to == pa[now]) continue;
		if (dep[to] == -1) {
			dep[to] = dep[now] + 1;
			pa[to] = now;
			dfs(to);
		}
		else if (dep[now] - dep[to] >= lim - 1) {
			cout << "2\n";
			cout << dep[now] - dep[to] + 1 << '\n';
			for (int i = now; i != pa[to]; i = pa[i]) cout << i << ' ';
			exit(0);
		}
	}
}

int main() {
	io(); cin >> n >> m;
	rep(i, 0, n) dep[i] = -1;
	dep[1] = 0;
	lim = ceil(sqrt(1.0 * n));
	rep(i, 1, m) {
		int x, y; cin >> x >> y;
		vec[x].emplace_back(y);
		vec[y].emplace_back(x);
	}
	dfs(1);
	cout << "1\n";
	rep(i, 0, (lim - 2)) {
		if (num[i] < lim) continue;
		int tot = lim;
		rep(j, 1, n) {
			if (dep[j] % (lim - 1) == i) {
				if (tot-- != lim) cout << ' ';
				cout << j;
				if (!tot) return 0;
			}
		}
	}
}
posted @ 2020-03-15 10:41  st1vdy  阅读(709)  评论(0编辑  收藏  举报