返回顶部

Codeforces Round #768 (Div. 2) 题解

作者:@cherish.
课程学习内容为作者从学校的PPT处摘抄,仅供自己学习参考,若需转载请注明出处:https://www.cnblogs.com/cherish-/p/15852779.html


D F未补

A. Min Max Swap

题目描述:给你两个长度为n的正整数数组a,b,你可以执行以下操作任意次:

  • 找到一个下标i
  • 交换aibi的值

求执行操作后的数组a的最大值与数组b的最大值的乘积的最小值。

思路:将所有ai>bi的位置的两元素交换位置,然后分别求两数组的最大值再相乘即可得乘积最小。

时间复杂度:O(n)

参考代码:

void solve() {
	int n;
	cin >> n;
	vector<int>a(n + 1, 0), b(n + 1, 0);
	for (int i = 1; i <= n; ++i) cin >> a[i];
	for (int i = 1; i <= n; ++i) cin >> b[i];
	for (int i = 1; i <= n; ++i) if (a[i] > b[i]) swap(a[i], b[i]);
	int res = (*max_element(a.begin() + 1, a.end())) * (*max_element(b.begin() + 1, b.end()));
	cout << res << '\n';
	return;
}

B. Fun with Even Subarrays

题目描述:给你一个长度为n的数组a,每次一个选择一个左边界lr和一个长度k,满足lr+2k1n。然后将alr+k+i的值赋值给alr+i,0i<k。问将数组所有元素变相等的最小操作次数。

思路:显然最终数组的所有元素的值都等于an,故按照an进行贪心即可。为了好操作,将数组翻转一下从前往后遍历贪心。

时间复杂度:O(n)

参考代码:

void solve() {
	int n;
	cin >> n;
	vector<int>a(n + 1, 0);
	int cnt = 0, res = 0;
	for (int i = 1; i <= n; ++i) cin >> a[i];
	reverse(a.begin() + 1, a.end());
	for (int i = 1; i <= n; ++i) {
		if (i <= cnt) continue;
		if (a[i] == a[1]) {
			++cnt;
			continue;
		}
		++res;
		cnt <<= 1;
	}
	cout << res << '\n';
	return;
}

C. And Matching

题目描述:给你一个长度为n的排列0n1,保证n=2i(0i<)。将这个排列中的数分成n2组,再给定正整数k,假定每一组的元素由(a,b)表示,若存在一种分组方式使得等式:

i=1n2ai&bi=k

成立,则输出这种分组方式,否则输出1

思路:注意到i&(ni1)=0,那么若kn1,那么可以将k0交换位置让kn1组成一对,就可以满足题设。当k=n1时,我们可以这样构造,让$ n - 1n - 2$为一组,那么还差1,我们可以让13为一组,然后让0n4一组,2n3一组剩下的还是按照i与$ n - i - 1使n = 4,k = 3$时不能这样划分,需要特判。

时间复杂度:O(n)

参考代码:

void solve() {
	int n, k;
	cin >> n >> k;
	if (k == n - 1) {
		if (n == 4) {
			cout << -1 << '\n';
			return;
		}
		--n;
		cout << n - 1 << ' ' << n << '\n';
		cout << 0 << ' ' << n - 3 << '\n';
		cout << 1 << ' ' << 3 << '\n';
		cout << 2 << ' ' << n - 2 << '\n';
		for (int i = 4; i < (n + 1) / 2; ++i) cout << i << ' ' << n - i << '\n';
		return;
	}
	vector<vector<int>>res;
	int idx = 0;
	for (int i = 0; i < n / 2; ++i) {
		res.push_back({ i , n - i - 1 });
		if (i == k) idx = res.size() - 1;
		if (n - i - 1 == k) idx = res.size() - 1;
	}
	res[0][0] = k;
	if (res[idx][0] == k) res[idx][0] = 0;
	else res[idx][1] = 0;
	for (auto re : res) cout << re[0] << ' ' << re[1] << '\n';
	return;
}

E. Paint the Middle

题目描述:给你两个长度为n的序列ac,初始时ci=01in。你可以执行以下操作任意次:

  • 选择三个下标i,j,k(1i<j<kn)
  • ai=ak
  • ci=cj=ck=0
  • cj赋值成1

i=1nci的最大值。

数据范围:3n2×105,1ain

思路:我们可以在读入的时候使用last数组记录一下每个数字出现的最后的下标,考虑从前往后遍历,如果当前枚举的元素ai最后一次的出现位置为j,则说明区间(i,j)内的值都可以取得,如果(i,j)存在k,使得alast[k]>j,那么(j,k)之间的元素也都可以取得,那么为了让取得的区间最大,就需要找到下标最大的位置,所以我们在遍历(i,j)之间的元素的时候,应该记录一下其对应元素最后一次出现的的位置的最大值,也即

maxk=i+1j1last[ak]

在当前区间被枚举完的时候,使用该值去更新新的区间。

时间复杂度:O(n)

参考代码:

void solve() {
	int n;
	cin >> n;
	vector<int>last(n + 1, 0), a(n + 1, 0);
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
		last[a[i]] = i;
	}
	int res = 0, pre = 0, cur = 0;
	for (int i = 1; i <= n; ++i) {
		pre = max(pre, last[a[i]]);
		if (cur > i) ++res;
		else cur = pre;
	}
	cout << res << '\n';
	return;
}
posted @   cherish-lgb  阅读(194)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示