|

Codeforces Round 1040 (Div. 2) A~D题解

A.Submission is All You Need

一、题意简述

给你一个长为 \(n\) 的序列 \(a\),你可以把序列中若干元素求和,或取 mex,求累加答案最大值。

二、思路

我们可以先分析 mex 操作。

我们很容易证明除了 \(0,0,...,0\)\(0,1,2,...,n\) 以外的所有序列取 mex 一定会变小,而 \(0,1,2,...,n\) 取 mex 得到的答案为 \(n+1\),显然没有直接求和更优,所以除了 \(0\) 取 mex,其他都要求和。

\(0,0,0,...,0\) 分开取 mex 肯定是要比一起去更优,如 0 0 0 0 取 mex 为 1,而分成 4 个 0 取 mex 为 4。

三、结论&代码

所以答案很明显,把所有 0 改为 1(mex 操作),然后统一求和。

关键代码:

inline void solve() 
{
	int n;cin >> n;
	ll ans = 0;
	rep(i,1,n,1) {
		int x;cin >> x;
		ans += max(1,x);
	}
	cout << ans << endl;
}

B.Pathless

一、题意简述

给你一个由 \(0,1,2\) 组成的序列 \(a\),你最开始的位置 \(x=1\),你可以通过 \(x-1\)\(x+1\) 走到 \(n\),同时你会得到 \(a_x\) 个积分,问是否有办法让我无论怎样走都不能恰好得到 \(s\) 个积分,输出方案,无解输出 -1。

二、思路

首先,对于 \(\sum a_i > s\) 的情况,怎样排都可以。

然后,对于 \(\sum a_i = s\) 的情况,输出 -1。

派出了以上两种情况,我们就可以讨论 \(\sum a_i < s\) 的情况了。

注意到一定会出现 0 11 2 至少其中之一,那么 \(sum - s = 3k\) 就能被构造出来了,可以通过 \(k\)1 2\(3k\)0 1 来解决。

同理,会出现 0 10 2 至少其中之一,那么 \(sum - s = 2k\) 就能被构造出来了。

根据斐蜀定理的推论,我们能同时构造出 2 的倍数和 3 的倍数,就一定能构造出所有 \(> 2 \times 3 - 2 - 3\) 的数,及构造出所有大于 1 的数。

三、结论&代码

所以只有当 \(s - sum = 1\) 时,可以构造,这是我采用的构造方法:000...0111...1222...2

核心代码:

void solve() {
	int n, s;
	cin >> n >> s;
	vector<int> a(n + 1, 0);
	int sum = 0;
	int zero = 0;
	int one = 0;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		sum += a[i];
		if (a[i] == 0) zero++;
		if (a[i] == 1) one++;
	}
	if (sum == s) {
		cout << -1 << endl;
		return;
	}
	if (sum > s || sum == s - 1) {
		for (int i = 1; i <= zero; i++) cout << "0 ";
		int x = n - zero - one;
		while (x--) cout << "2 ";
		for (int i = 1; i <= one; i++) cout << "1 ";
		cout << endl;
		return;
	}
	cout << "-1" << endl;
}

C.Double Perspective

一、题意简述

给你 \(n\) 个数对 \([a_i,b_i]\),让你从中选若干个,使得区间覆盖的面积减去环的个数最大。

二、思路

我们假设现在由一条 \([a_i,b_i]\) 的边,那么我们要让覆盖的面积最大,就看这一段是否被覆盖过了。

  • 被覆盖过:肯定不会增加覆盖的面积,但是由于 \([a_i,b_i]\) 中多了一条边,对于环的个数不会减小,还可能会增加,所以此时的最优策略是不连。

  • 否则, \(b_i\) 一定是一个新节点或 \(a_i,b_i\) 并不在一个连通块内,此时环的个数不会增加,因为连通块之间只有一条边连着,所以此时的最优策略是连。

三、结论&代码

代码就很简单了,注意并查集别写错了

核心代码:

void init() {
	for (int i = 0; i < N; i++) fa[i] = i;
}
int find(int x) {
	if (x == fa[x]) return x;
	return fa[x] = find(fa[x]);
}
bool is(int x, int y) {
	x = find(x);
	y = find(y);
	return (x == y ? true : false);
}
void join(int x, int y) {
	x = find(x);
	y = find(y);
	if (x == y) return ;
	fa[x] = y;
}
void solve() {
	init();
	vector<int> ans;
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int u, v;
		cin >> u >> v;
		if (!is(u, v)) {
			ans.push_back(i);
			join(u, v);
		}

	}
	cout << ans.size() << endl;
	for (auto &i : ans) cout << i << ' ';
	cout << endl;
}

D.Stay or Mirror

一、题意简述

给你一个长为 \(n\)排列 \(p\),你需要构造一个序列 \(a\)\(a_i=p_i\)\(a_i=2n-p_i\),使得序列 \(a\) 的逆序对尽可能少。

二、思路

首先我们要明确:\(p \le n\),那么 \(2p \le 2n,p\le2n - p\),所以我们选第二个相当于在放大 \(p_i\)

为了方便,我们设 \(l_i\) 为第 \(1\sim i-1\) 元素比 \(p_i\) 大的数量,\(r_i\)\(i+1\sim n\)\(p_i\) 小的数量。

我们考虑当第 \(i\) 个位置选 x 时的贡献:

  • 前面,所有大于 \(x\) 的,贡献为 \(l_i\)

由于 \(2n - x\) 已经大于 \(n\) 了,所以 \(l\) 的贡献为 \(0\),所以变换后的贡献是 x 后的:

  • 后面,所有大于 \(2n-x\) 的,贡献为 \(r_i\)

证明:原来小于 \(x\) 的数会在遍历到他的时候再计算,而原来小于 \(x\) 的数在 \(x\) 变为 \(2n-x\) 的时候都会小于 \(2n-x\),所以这时候的贡献就为原来右边大于 \(2n-x\) 的元素的个数。

三、结论&代码

对于第 \(i\) 个数,选择 \(\min (l_i,r_i)\) 进行累加。

核心代码:

void slove() {
	ll n;
	cin >> n;
	for (ll i = 1; i <= n; i++) {
		cin >> s[i];
	}
	ll ans = 0;
	for (ll i = 1; i <= n; i++) {
		ll l = 0, r = 0;
		for (ll j = 1; j < i; j++)
			if (s[j] > s[i])l++;
		for (ll j = i + 1; j <= n; j++)
			if (s[j] > s[i])r++;
		ans += min(l, r);
	}
	cout << ans << endl;
}
posted @ 2025-08-18 11:36  _q_ak  阅读(20)  评论(0)    收藏  举报