【CodeForces】CodeForces Global Round 3 题解

【比赛链接】

【题解链接】

【A】 Another One Bites The Dust

【思路要点】

  • 答案为 2 c + 2 m i n { a , b } + [ a ≠ b ] 2c+2min\{a,b\}+[a\ne b] 2c+2min{a,b}+[a̸=b]
  • 时间复杂度 O ( 1 ) O(1) O(1)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int main() {
	ll a, b, c;
	read(a), read(b), read(c);
	ll ans = 2 * min(a, b);
	a -= ans / 2, b -= ans / 2, ans += 2 * c;
	if (a + b) ans += 1;
	writeln(ans);
	return 0;
}

【B】 Born This Way

【思路要点】

  • 最优的方案一定是选取一段 A A A 的前缀,再选取剩余可达的最小的若干个 B B B
  • 枚举选取的前缀即可,注意 A A A 的前缀可以为空。
  • 时间复杂度 O ( N + M ) O(N+M) O(N+M)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, m, ta, tb, k;
int a[MAXN], b[MAXN];
int main() {
	read(n), read(m), read(ta), read(tb), read(k);
	for (int i = 1; i <= n; i++)
		read(a[i]), a[i] += ta;
	for (int i = 1; i <= m; i++)
		read(b[i]);
	int pos = 0, ans = k;
	if (k >= n) {
		puts("-1");
		return 0;
	}
	for (int i = 0; i <= k; i++) {
		while (pos < m && a[i + 1] > b[pos + 1]) pos++;
		chkmax(ans, pos + k - i);
	}
	if (ans >= m) puts("-1");
	else writeln(b[ans + 1] + tb);
	return 0;
}

【C】 Crazy Diamond

【思路要点】

  • 利用 1 , N 1,N 1,N 2 , 3 , … , N − 1 2,3,\dots,N-1 2,3,,N1 交换至应有的位置上即可。
  • 时间复杂度 O ( N ) O(N) O(N) ,操作次数在 3 N 3N 3N 以内。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, a[MAXN], home[MAXN];
vector <pair <int, int>> ans;
void check(int x, int y) {
	ans.emplace_back(x, y);
	swap(home[a[x]], home[a[y]]);
	swap(a[x], a[y]);
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]), home[a[i]] = i;
	for (int i = 2; i <= n - 1; i++)
		if (home[i] != i) {
			int pos = home[i];
			if (2 * abs(pos - 1) >= n) {
				check(pos, 1);
				if (2 * abs(1 - i) >= n) check(1, i);
				else {
					check(1, n);
					check(n, i);
				}
			} else {
				check(pos, n);
				if (2 * abs(n - i) >= n) check(n, i);
				else {
					check(1, n);
					check(1, i);
				}
			}
		}
	if (home[1] != 1) check(1, n);
	writeln(ans.size());
	for (auto x : ans)
		printf("%d %d\n", x.first, x.second);
	return 0;
}

【D】 Dirty Deeds Done Dirt Cheap

【思路要点】

  • 将数对分为 x &gt; y x&gt;y x>y 的数对和 x &lt; y x&lt;y x<y 的数对。
  • 对于 x &lt; y x&lt;y x<y 的数对,按照 x x x 降序排序,可以使得其全部使用。
  • 对于 x &gt; y x&gt;y x>y 的数对,按照 y y y 升序排序,可以使得其全部使用。
  • 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, a[MAXN], b[MAXN];
vector <int> inc, dec;
bool cmp(int x, int y) {
	return a[x] > a[y];
}
bool cnp(int x, int y) {
	return b[x] < b[y];
}
int main() {
	read(n);
	vector <int> inc, dec;
	for (int i = 1; i <= n; i++) {
		read(a[i]), read(b[i]);
		if (a[i] < b[i]) inc.push_back(i);
		else dec.push_back(i);
	}
	if (inc.size() > dec.size()) {
		writeln(inc.size());
		sort(inc.begin(), inc.end(), cmp);
		for (auto x : inc)
			printf("%d ", x);
		printf("\n");
	} else {
		writeln(dec.size());
		sort(dec.begin(), dec.end(), cnp);
		for (auto x : dec)
			printf("%d ", x);
		printf("\n");
	}
	return 0;
}

【E】 Earth Wind and Fire

【思路要点】

  • 排序两个数组,记 d i = s i − t i d_i=s_i-t_i di=siti ,则首先要求 ∑ d i = 0 \sum d_i=0 di=0
  • 可以发现一次操作使得靠前的一个 d i d_i di 增加,靠后的一个 d i d_i di 减少,最终要求全部的 d i d_i di 变为零。
  • 可以直接采用类似括号序列的方式贪心。
  • 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct info {int x, y; ll z; };
vector <info> ans;
ll s[MAXN], t[MAXN], p[MAXN], a[MAXN];
bool cmp(int x, int y) {
	return s[x] < s[y];
}
int main() {
	int n; read(n);
	for (int i = 1; i <= n; i++)
		read(s[i]), p[i] = i;
	for (int i = 1; i <= n; i++)
		read(t[i]);
	sort(t + 1, t + n + 1);
	sort(p + 1, p + n + 1, cmp);
	sort(s + 1, s + n + 1);
	int sum = 0;
	for (int i = 1; i <= n; i++) {
		a[i] = s[i] - t[i];
		sum += a[i];
	}
	if (sum != 0) {
		puts("NO");
		return 0;
	}
	int pos = 0;
	for (int i = 1; i <= n; i++) {
		if (a[i] > 0) {
			puts("NO");
			return 0;
		}
		while (a[i] < 0) {
			while (a[pos] <= 0) pos++;
			int d = min(-a[i], a[pos]);
			ans.push_back((info) {p[i], p[pos], d});;
			a[i] += d, a[pos] -= d;
		}
	}
	puts("YES");
	writeln(ans.size());
	for (auto x : ans)
		printf("%d %d %lld\n", x.x, x.y, x.z);
	return 0;
}

【F】 Foo Fighters

【思路要点】

  • s s s 的第 i i i 位设为 1 1 1 等价于取反 m a s k mask mask i i i 位为 1 1 1 的数的权值。
  • 将各数按照 m a s k mask mask 最高数位 i i i 分类,则我们可以用 s s s 的第 i i i 位保证各组数的权值和非正/非负,同时由于数据保证初始权值和非零,我们得到的结果也一定非零。
  • 时间复杂度 O ( N L o g V ) O(NLogV) O(NLogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n;
ll a[MAXN], mask[MAXN], s;
int main() {
	read(n);
	for (int i = 1; i <= n; i++) {
		read(a[i]), read(mask[i]);
		s += a[i];
	}
	if (s < 0) {
		for (int i = 1; i <= n; i++)
			a[i] = -a[i];
	}
	ll ans = 0;
	for (int p = 0; p <= 61; p++) {
		ll bit = 1ll << p, cur = 0;
		for (int i = 1; i <= n; i++)
			if ((mask[i] & bit) && (mask[i] < (2 * bit))) cur += a[i];
		if (cur > 0) {
			ans ^= bit;
			for (int i = 1; i <= n; i++)
				if (mask[i] & bit) a[i] = -a[i];
		}
	}
	writeln(ans);
	return 0;
}

【G】 Gold Experience

【思路要点】

  • a i a_i ai 各质因数的指数设为一,答案不变。
  • 计算一个位置的出边个数可以使用莫比乌斯反演。
  • 找到一个与至少两个点没有边相连的点 x x x
  • 若无法找到该 x x x ,则给定图的补图仅由大小为 1 , 2 1,2 1,2 的联通块构成,不难选出一个大小在 N 2 \frac{N}{2} 2N 以内的独立集。
  • 否则,首先删除 x x x ,以及与 x x x 没有边相连的两个点 y , z y,z y,z
  • 记剩余点集合为 R R R ,若 R R R 中存在至少 k k k 个与其余点均有边的点,则可以选取它们作为一个团。
  • 否则,二分出 p o s pos pos 满足 { 1 , 2 , … , p o s } \{1,2,\dots,pos\} {1,2,,pos} 中尚不能选出 k − 3 k-3 k3 个点,满足各个点与选出的至少一个点没有边相连,而 { 1 , 2 , … , p o s + 1 } \{1,2,\dots,pos+1\} {1,2,,pos+1} 中可以。
  • S S S { 1 , 2 , … , p o s } \{1,2,\dots,pos\} {1,2,,pos} 中选出的集合, T T T { 1 , 2 , … , p o s + 1 } \{1,2,\dots,pos+1\} {1,2,,pos+1} 中选出的集合。
  • 删除 T T T 中新出现的与 p o s + 1 pos+1 pos+1 没有边相连的点,直到 ∣ T ∣ = k − 2 |T|=k-2 T=k2 ,再将初始时找到的 x , y x,y x,y 加入 T T T 中即可。
  • 时间复杂度 O ( N L o g N × 2 8 ) O(NLogN\times 2^{8}) O(NLogN×28)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXV = 1e7 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int tot, prime[MAXV], f[MAXV], miu[MAXV];
void sieve(int n) {
	miu[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (f[i] == 0) prime[++tot] = f[i] = i, miu[i] = -1;
		for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			f[tmp] = prime[j];
			if (prime[j] == f[i]) miu[tmp] = 0;
			else miu[tmp] = -miu[i];
		}
	}
}
int func(int x) {
	int ans = 1;
	while (x != 1) {
		int tmp = f[x]; ans *= f[x];
		while (x % tmp == 0) x /= tmp;
	}
	return ans;
}
int n, k, a[MAXN], b[MAXN], home[MAXN], cnt[MAXV];
bool iskey[MAXN];
vector <int> d[MAXV];
void work(int x) {
	memset(cnt, 0, sizeof(cnt));
	for (int i = 1; i <= x; i++)
		if (!iskey[i]) {
			for (auto x : d[a[i]])
				cnt[x]++;
		}
	for (int i = 1; i <= x; i++)
		if (!iskey[i]) {
			b[i] = 0;
			for (auto x : d[a[i]])
				b[i] += miu[x] * cnt[x];
		}
}
int main() {
	read(n), read(k), sieve(1e7);
	for (int i = 1; i <= n; i++) {
		read(a[i]), home[i] = i, a[i] = func(a[i]);
		d[a[i]].clear();
		for (int j = 1; j * j <= a[i]; j++)
			if (a[i] % j == 0) {
				cnt[j]++, d[a[i]].push_back(j);
				if (j * j != a[i]) cnt[a[i] / j]++, d[a[i]].push_back(a[i] / j);
			}
	}
	int pos = 0;
	for (int i = 1; i <= n; i++) {
		for (auto x : d[a[i]])
			b[i] += miu[x] * cnt[x];
		if (b[i] >= 2) pos = i;
	}
	if (pos == 0) {
		set <int> s;
		memset(cnt, 0, sizeof(cnt));
		for (int i = 1; i <= n; i++) {
			b[i] = 0;
			for (auto x : d[a[i]])
				b[i] += miu[x] * cnt[x];
			if (b[i] == 0 && s.size() < k) {
				s.insert(i);
				for (auto x : d[a[i]])
					cnt[x]++;
			}
		}
		for (auto x : s)
			printf("%d ", x);
		return 0;
	}
	vector <int> key;
	key.push_back(pos), iskey[pos] = true;
	for (int i = 1; i <= n; i++)
		if (__gcd(a[i], a[pos]) == 1 && key.size() < 3) key.push_back(i), iskey[i] = true;
	work(n);
	int zeros = 0;
	for (int i = 1; i <= n; i++)
		if (!iskey[i] && b[i] == 0) zeros++;
	if (zeros >= k) {
		for (int i = 1; i <= n; i++)
			if (!iskey[i] && b[i] == 0 && k != 0) {
				printf("%d ", i);
				k--;
			}
	} else {
		int l = 0, r = n;
		while (l < r) {
			int mid = (l + r) / 2;
			int cnt = 3; work(mid);
			for (int i = 1; i <= mid; i++)
				if (!iskey[i] && b[i] != 0) cnt++;
			if (cnt >= k) r = mid;
			else l = mid + 1;
		}
		set <int> s; work(l);
		for (auto x : key)
			s.insert(x);
		for (int i = 1; i <= l; i++)
			if (!iskey[i] && b[i] != 0) s.insert(i);
		if (s.size() == k) {
			for (auto x : s)
				printf("%d ", x);
		} else {
			set <int> t; work(l - 1);
			for (auto x : key)
				t.insert(x);
			for (int i = 1; i <= l - 1; i++)
				if (!iskey[i] && b[i] != 0) t.insert(i);
			t.insert(l);
			bool flg = false;
			for (int i = 1; i <= l - 1 && (!flg || t.size() < k); i++)
				if (__gcd(a[l], a[i]) == 1) t.insert(i), flg = true;
			if (t.size() > k) t.erase(key.back());
			for (auto x : t)
				printf("%d ", x);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值