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 << '
';
}
}
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() << '
';
}
}
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)] << '
';
else cout << cnt++ << '
';
}
}
D. Ehab the Xorcist
题意:给定两个数字 (u) 和 (v) ,构造一个最短的数列 (a_n) 满足 (a_1oplus a_2oplus ... 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 << '
' << m;
else {
if ((m - n) % 2 == 0) {
ll x = n, y = (m - n) / 2;
ll z = (x ^ y);
if (z + y == m) cout << "2
" << z << ' ' << y;
else cout << "3
" << x << ' ' << y << ' ' << y;
}
else cout << -1;
}
}
E. Ehab's REAL Number Theory Problem
题意:给定一个数组 (a_n) ,数组中任意一个元素的因子数不超过 (7) ,找出一个最短的子序列,满足该子序列之积为完全平方数。
分析:由于任意元素因子数不超过 (7) ,因此 (a_i) 的质因子数 (leq 2) (如果质因子数为 (3) 将有 (8) 个因子)。因此我们先将所有给定元素进行预处理,去除所有平方因子(假设一个元素为 (2^2cdot 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} ceil) 的独立集,或者找出一个点数 (geqlceil sqrt{n} ceil) 的环。
分析:和 (CF1103C) 有异曲同工之妙,这类题型都可以通过 (dfs) 树给出构造。首先,找环是很容易的,直接在 (dfs) 树上找返祖边(回边)即可,如果存在点数 (geqlceil sqrt{n} ceil) 的环就直接输出。然后再考虑独立集的构造,我们用 (lceil sqrt{n} ceil-1) 种颜色,给 (dfs) 树上的节点按照 (dep[i]\%(lceil sqrt{n} ceil-1)) 的规则染色,由于 (dfs) 树的特性,相同深度的节点必定构成一个独立集,而在模意义下相同的任意两个不同深度节点之间,如果存在直连边就说明存在一个节点数 (geqlceil sqrt{n} ceil) 的环。因此我们只需要找到一个点数 (geqlceil sqrt{n} ceil) 的集合并恰好输出 (lceil sqrt{n} ceil) 个点。
#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
";
cout << dep[now] - dep[to] + 1 << '
';
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
";
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;
}
}
}
}