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;
}
}
}
}