【CodeForces】Codeforces Global Round 9

比赛链接

点击打开链接

官方题解

点击打开链接

Problem A. Sign Flipping

将奇数位的数取非正值,偶数位的数取非负值即可。

单组数据时间复杂度 O ( N ) O(N) O(N)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
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;
}
int main() {
	int T; read(T);
	while (T--) {
		int n; read(n);
		for (int i = 1; i <= n; i++) {
			int x; read(x);
			if (i & 1) printf("%d ", -abs(x));
			else printf("%d ", abs(x));
		}
		printf("\n");
	}
	return 0;
}

Problem B. Neighbor Grid

若一个格子上初始时的数大于与其相邻的格子数,则答案显然为 No 。
否则,将所有格子上的数增加至与其相邻的格子数一定会形成一组合法解。

单组数据时间复杂度 O ( N × M ) O(N\times M) O(N×M)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 305;
const int dx[4] = {0, 0, 1, -1};
const int dy[4] = {1, -1, 0, 0};
typedef long long ll;
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;
}
int n, m, a[MAXN][MAXN], d[MAXN][MAXN];
int main() {
	int T; read(T);
	while (T--) {
		read(n), read(m);
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			read(a[i][j]), d[i][j] = 0;
		for (int i = 1; i <= n - 1; i++)
		for (int j = 1; j <= m; j++)
			d[i][j]++, d[i + 1][j]++;
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m - 1; j++)
			d[i][j]++, d[i][j + 1]++;
		bool ans = true;
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			if (a[i][j] > d[i][j]) ans = false;
		if (ans) {
			puts("YES");
			for (int i = 1; i <= n; i++) {
				for (int j = 1; j <= m; j++)
					printf("%d ", d[i][j]);
				printf("\n");
			}
		} else puts("NO");
	}
	return 0;
}

Problem C. Element Extermination

答案为 Yes 当且仅当 a 1 ≤ a N a_1\leq a_N a1aN

考虑其必要性,若 a 1 > a N a_1>a_N a1>aN ,则无论如何操作,序列中第一个元素将始终大于最后一个元素,从而不可能使序列的长度减少至 2 2 2 以下。考虑其充分性,则在保证 a 1 ≤ a N a_1\leq a_N a1aN 的情况下任意操作即可构造出一组消除方案,由于 a 1 ≤ a N a_1\leq a_N a1aN ,序列长度 ≥ 2 \geq2 2 的时候,一定存在可以操作的位置。

单组数据时间复杂度 O ( N ) O(N) O(N)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
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;
}
int main() {
	int T; read(T);
	while (T--) {
		int n; read(n);
		int x = 0, y = 0;
		for (int i = 1; i <= n; i++) {
			int z; read(z);
			if (i == 1) x = z;
			else y = z;
		}
		if (x < y) puts("YES");
		else puts("NO");
	}
	return 0;
}

Problem D. Replace by MEX

考虑通过操作序列使得 a i = i − 1 a_i=i-1 ai=i1

令序列当前的 Mex 为 x   ( x ≤ N ) x\ (x\leq N) x (xN) ,显然,序列中此时不存在元素 x x x
x < N x<N x<N ,则说明 a x + 1 ≠ x a_{x+1}\ne x ax+1=x ,令 a x + 1 = x a_{x+1}=x ax+1=x 即可。
x = N x=N x=N ,则任意操作一个尚不满足 a i = i − 1 a_i=i-1 ai=i1 的位置。

单组数据时间复杂度 O ( N 2 ) O(N^2) O(N2) ,操作次数不超过 2 N 2N 2N

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
typedef long long ll;
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;
}
int n, a[MAXN];
int cur() {
	static int vis[MAXN], tag = 0; tag++;
	for (int i = 1; i <= n; i++)
		vis[a[i]] = tag;
	int ans = 0;
	while (vis[ans] == tag) ans++;
	return ans;
}
bool check() {
	for (int i = 2; i <= n; i++)
		if (a[i - 1] > a[i]) return false;
	return true;
}
int main() {
	int T; read(T);
	while (T--) {
		read(n);
		for (int i = 1; i <= n; i++)
			read(a[i]);
		vector <int> ans;
		while (!check()) {
			int val = cur();
			if (val != n) {
				a[val + 1] = val;
				ans.push_back(val + 1);
			} else {
				int pos = 0;
				for (int i = 1; i <= n; i++)
					if (a[i] != i - 1) pos = i;
				a[pos] = val;
				ans.push_back(pos);
			}
		}
		cout << ans.size() << endl;
		for (auto x : ans) printf("%d ", x);
		printf("\n");
	}
	return 0;
}

Problem E. Inversion SwapSort

考虑在不破坏后续元素的相对大小关系的情况下,将最小的元素移动至 a 1 a_1 a1 ,同时消耗掉所有的包含位置 1 1 1 的逆序对 ( 1 , x i ) (1,x_i) (1,xi) 贡献的操作。

则按照 a x i a_{x_i} axi 从大到小的顺序依次操作 ( 1 , x i ) (1,x_i) (1,xi) 即可。
由此,我们将问题缩小了规模,可以递归解决。

时间复杂度 O ( N 2 L o g N ) O(N^2LogN) O(N2LogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
typedef long long ll;
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;
}
int n, a[MAXN];
int main() {
	read(n);
	vector <pair <int, int>> ans;
	for (int i = 1; i <= n; i++)
		read(a[i]);
	for (int i = 1; i <= n; i++) {
		vector <int> p;
		for (int j = i + 1; j <= n; j++)
			if (a[j] < a[i]) p.push_back(j);
		sort(p.begin(), p.end(), [&] (int x, int y) {
			if (a[x] != a[y]) return a[x] > a[y];
			else return x > y;
		});
		for (auto x : p) ans.emplace_back(i, x);
	}
	cout << ans.size() << endl;
	for (auto x : ans) printf("%d %d\n", x.first, x.second);
	return 0;
}

Problem F. Integer Game

a < b < c a<b<c a<b<c ,考虑 1 1 1 号玩家的将军局面。
1 1 1 号玩家可以直接取胜当且仅当 2 2 2 号玩家上一次操作了 c c c ,且
b − a = c − b b-a=c-b ba=cb

1 1 1 号玩家可以采取如下策略保证自己获胜:
( 1 ) (1) (1) 、令 x = 1 0 9 + 7 x=10^9+7 x=109+7 ,保证在 2 2 2 号玩家完成操作后,被操作的数是最大的。
( 2 ) (2) (2) 、令 a < b < c , x = 2 c − b − a a<b<c,x=2c-b-a a<b<c,x=2cba ,由于 2 2 2 号玩家无法操作 c c c ,此时其无论如何操作,三个数都将形成一个等差数列,或是 b , c , 2 c − b b,c,2c-b b,c,2cb ,或是 a , c , 2 c − a a,c,2c-a a,c,2ca
( 3 ) (3) (3) 、令 a < b < c , x = c − b a<b<c,x=c-b a<b<c,x=cb ,可以保证 2 2 2 号玩家无法操作。

时间复杂度 O ( 1 ) O(1) O(1)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
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;
}
ll a[4]; int res;
void getres(int &res) {
	cin >> res;
	if (res == 0) exit(0);
}
ll gety() {
	ll Max = 0, ans = 0;
	for (int i = 1; i <= 3; i++)
		chkmax(Max, a[i]);
	for (int i = 1; i <= 3; i++)
		if (Max == a[i]) ans += a[i] * 2;
		else ans -= a[i];
	return ans;
}
int main() {
	read(a[1]), read(a[2]), read(a[3]);
	puts("First");
	int x = 1e9 + 7; cout << x << endl;
	getres(res); a[res] += x;
	ll y = gety(); cout << y << endl;
	getres(res); a[res] += y;
	sort(a + 1, a + 4);
	assert(a[2] - a[1] == a[3] - a[2]);
	cout << a[2] - a[1] << endl;
	getres(res), assert(false);
	return 0;
}

Problem G. Tree Modification

枚举最终形成的菊花的中心 x x x ,考虑如何以最少的操作次数完成转化。

令距离 x x x 为偶数的节点数为 A n s Ans Ans ,则不难构造出一种 A n s − 1 Ans-1 Ans1 次操作的方案。并且,考虑图的二分图染色,可以发现,一次操作仅改变了一个点的颜色,因此 A n s − 1 Ans-1 Ans1 也是操作次数的下界。

由此,答案即为将图二分图染色后,点数较少的颜色的点数 − 1 -1 1
由于写程序的时候没有分析透彻,下文的代码使用了换根树形 DP 的方式计算了以上数值。

时间复杂度 O ( N ) O(N) O(N)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
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;
}
int ans, dp[MAXN][2], sum[MAXN][2];
vector <int> a[MAXN];
void update(int pos) {
	dp[pos][0] = sum[pos][1] + 1;
	dp[pos][1] = sum[pos][0];
}
void getdp(int pos, int fa) {
	for (auto x : a[pos])
		if (x != fa) {
			getdp(x, pos);
			sum[pos][0] += dp[x][0];
			sum[pos][1] += dp[x][1];
		}
	update(pos);
}
void getans(int pos, int fa) {
	chkmin(ans, dp[pos][0] - 1);
	for (auto x : a[pos])
		if (x != fa) {
			sum[pos][0] -= dp[x][0];
			sum[pos][1] -= dp[x][1];
			update(pos);
			sum[x][0] += dp[pos][0];
			sum[x][1] += dp[pos][1];
			update(x);
			getans(x, pos);
			sum[x][0] -= dp[pos][0];
			sum[x][1] -= dp[pos][1];
			update(x);
			sum[pos][0] += dp[x][0];
			sum[pos][1] += dp[x][1];
			update(pos);
		}
}
int main() {
	int n; read(n);
	for (int i = 1; i <= n - 1; i++) {
		int x, y; read(x), read(y);
		a[x].push_back(y);
		a[y].push_back(x);
	}
	ans = n + 1;
	getdp(1, 0);
	getans(1, 0);
	cout << ans << endl;
	return 0;
}

Problem H. Set Merging

首先,考虑一个操作次数 N 2 N^2 N2 的解法,我们需要对所有区间,求出对应的集合。

考虑对权值分治,令 M = ⌊ N 2 ⌋ M=\lfloor\frac{N}{2}\rfloor M=2N ,首先求出数组 a i a_i ai 权值在 [ 1 , M ] [1,M] [1,M] 内的子序列所有区间对应的集合,以及数组 a i a_i ai 权值在 [ M + 1 , N ] [M+1,N] [M+1,N] 内的子序列所有区间对应的集合。此时,原数组的任意一个区间对应的集合都可以通过不少于一次操作获得。

考虑分析其操作次数 T ( N ) T(N) T(N) ,有
T ( N ) = ( N + 1 2 ) + T ( ⌊ N 2 ⌋ ) + T ( ⌈ N 2 ⌉ ) ≈ N 2 T(N)=\binom{N+1}{2}+T(\lfloor\frac{N}{2}\rfloor)+T(\lceil\frac{N}{2}\rceil)\approx N^2 T(N)=(2N+1)+T(2N)+T(2N)N2

对于原问题,考虑对权值进行分块处理,令块长为 B B B ,则总操作次数约为
N B × B 2 + Q × N B \frac{N}{B}\times B^2+Q\times \frac{N}{B} BN×B2+Q×BN

那么,令 B = Q B=\sqrt{Q} B=Q 即可。
时间复杂度 O ( N 2 + N Q ) O(N^2+N\sqrt{Q}) O(N2+NQ ) ,操作次数约为 2 N Q 2N\sqrt{Q} 2NQ

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
const int MAXQ = 2e5 + 5;
const int Limit = 2.2e6;
typedef long long ll;
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;
}
struct info {
	int l[MAXN], r[MAXN];
	vector <vector <int>> home;
	void init(int x) {
		home.resize(x + 1);
		for (int i = 0; i <= x; i++)
			home[i].resize(x + 1);
	}
	int query(int ql, int qr) {
		if (l[ql] > r[qr]) return 0;
		else return home[l[ql]][r[qr]];
	}
} res[MAXN];
int n, q, size, a[MAXN], b[MAXN], k[MAXQ];
int tot, block, ql[MAXN], qr[MAXN], belong[MAXN];
vector <pair <int, int>> ans;
int merge(int x, int y) {
	if (x == 0 || y == 0) return x + y;
	ans.emplace_back(x, y);
	assert(++size <= Limit);
	return size;
}
info getres(int l, int r) {
	if (l == r) {
		info ans;
		for (int i = 1; i <= n; i++) {
			ans.l[i] = 1 + (i > b[l]);
			ans.r[i] = (i >= b[r]);
		}
		ans.init(1);
		ans.home[1][1] = b[l];
		return ans;
	}
	int mid = (l + r) / 2;
	info ans, ansl, ansr;
	ansl = getres(l, mid);
	ansr = getres(mid + 1, r);
	for (int i = 1, j = 1, k = 0; i <= n; i++) {
		k += (l <= a[i] && a[i] <= r);
		ans.l[i] = j, ans.r[i] = k;
		j += (l <= a[i] && a[i] <= r);
	}
	ans.init(r - l + 1);
	for (int i = 1; i <= n; i++)
		if (l <= a[i] && a[i] <= r) {
			for (int j = i; j <= n; j++)
				if (l <= a[j] && a[j] <= r) {
					ans.home[ans.l[i]][ans.r[j]] = merge(ansl.query(i, j), ansr.query(i, j));
				}
		}
	return ans;
}
int main() {
	read(n), read(q), size = n;
	for (int i = 1; i <= n; i++)
		read(a[i]), b[a[i]] = i;
	block = 1 << 8;
	for (int i = 1; i <= n; i++) {
		if (i % block == 1 % block) ql[++tot] = i;
		qr[tot] = i, belong[i] = tot;
	}
	for (int i = 1; i <= tot; i++)
		res[i] = getres(ql[i], qr[i]);
	for (int i = 1; i <= q; i++) {
		int ql, qr, cur = 0; read(ql), read(qr);
		for (int j = 1; j <= tot; j++)
			cur = merge(cur, res[j].query(ql, qr));
		k[i] = cur;
	}
	printf("%d\n", size);
	for (auto x : ans)
		printf("%d %d\n", x.first, x.second);
	for (int i = 1; i <= q; i++)
		printf("%d ", k[i]);
	printf("\n");
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值