Codeforces Round 1052 (Div. 2)


A. Equal Occurrences

题意:求\(a\)的一个最长子序列,使得每个数出现的次数相同。

记录每个数出现的次数,排序后从小到大枚举出现次数,那么比它多的数都可以选。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	std::vector<int> cnt(n + 1);
	for (auto & x : a) {
		++ cnt[x];
	}

	std::ranges::sort(cnt);
	int ans = 0;
	for (int i = 1; i <= n; ++ i) {
		ans = std::max(ans, cnt[i] * (n - i + 1));
	}
	std::cout << ans << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

B. Merging the Sets

题意:若干个集合,你可以选择一些使得它们的并集里\([1, m]\)里的数都出现过。求有没有至少三种选法。

如果所有集合的并集都不行,则无解。
否则选择所有集合是一种选法,然后我们枚举集合,如果删掉这个集合还是合法的,就多一种选法。只需要记录每个数出现的次数就能判断。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, m;
	std::cin >> n >> m;
	std::vector<int> cnt(m + 1);
	std::set<int> s;
	std::vector<std::vector<int>> a(n);
	for (int i = 0; i < n; ++ i) {
		int k;
		std::cin >> k;
		a[i].assign(k, 0);
		for (int j = 0; j < k; ++ j) {
			std::cin >> a[i][j];
			s.insert(a[i][j]);
			++ cnt[a[i][j]];
		}
	}

	if(s.size() < m) {
		std::cout << "NO\n";
		return;
	}

	int tot = 0;
	for (int i = 0; i < n; ++ i) {
		bool flag = true;
		for (auto & x : a[i]) {
			if ( -- cnt[x] <= 0) {
				flag = false;
			}
		}

		if (flag) {
			++ tot;
		}

		for (auto & x : a[i]) {
			++ cnt[x];
		}
	}

	if (tot >= 2) {
		std::cout << "YES\n";
	} else {
		std::cout << "NO\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

C. Wrong Binary Search

题意:一个二分查找代码,每次在\([l, r]\)里随机一个数\(m\),如果\(p[m] == x\)返回\(m\),p[m] < x\(则\)r = m - 1\(,否则\)l = m + 1$。构造一个排列,需要你有些数能正确找到有些不能。

如果一个数不能被正确找到,那么它前面或者后面肯定不是有序的。
那么对于\(s_i = 1\)的位置\(p_i = i\)。对于使得\(i \in [l, r]\)\(s_i = 0\)的区间\([l, r]\),从大到小填就行。\(l = r\)无解。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::string s;
	std::cin >> s;
	std::vector<int> ans(n);
	for (int i = 0; i < n; ++ i) {
		if (s[i] == '1') {
			ans[i] = i;
		} else {
			int j = i;
			while (j + 1 < n && s[j + 1] == '0') {
				++ j;
			}

			if (i == j) {
				std::cout << "NO\n";
				return;
			}

			for (int k = i, x = j; k <= j; ++ k) {
				ans[k] = x -- ;
			}
			i = j;
		}
	}

	std::cout << "YES\n";
	for (int i = 0; i < n; ++ i) {
		std::cout << ans[i] + 1 << " \n"[i == n - 1];
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

D1 && D2. Max Sum OR

题意:一个长度为\(n = r - l + 1\)的数组,\(a_i = i + l, i \in [1, n]\),将它重新排列,使得\(\sum_{i=1}^{n} a_i | (i + l)\)最大,其\(|\)是位运算或。

\(01trie\)\([l, r]\)的每一个数,然后贪心查询每个数在剩下的数里和它或起来最大的数。如果两个数互相和对方或起来最大,就让他们匹配,然后把这两个数从字典树里删掉。一直到匹配完就行。不会证明,赛时猜的。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

const int N = 2e5 + 5;

int Tr[N * 30][2], cnt[N * 30];
struct Trie {
	int idx;
	Trie() {
		idx = 0;
		Tr[0][0] = Tr[0][1] = 0;
		cnt[idx] = 0;
	}

	int new_node() {
		++ idx;
		Tr[idx][0] = Tr[idx][1] = 0;
		cnt[idx] = 0;
		return idx;
	}

	void insert(i64 x) {
		int p = 0;
		for (i64 i = 29; i >= 0; -- i) {
			i64 s = x >> i & 1;
			if (!Tr[p][s]) {	
				Tr[p][s] = new_node();
			}

			p = Tr[p][s];
			++ cnt[p];
		}
	}

	i64 query(i64 x) {
		i64 res = 0;
		int p = 0;
		for (i64 i = 29; i >= 0; -- i) {
			i64 s = x >> i & 1;
			if (s == 0) {
				if (Tr[p][1] && cnt[Tr[p][1]]) {
					res += 1ll << i;
					p = Tr[p][1];
				} else {
					p = Tr[p][0];
				}
			} else {
				if (Tr[p][0] && cnt[Tr[p][0]]) {
					p = Tr[p][0];
				} else {
					res += 1ll << i;
					p = Tr[p][1];
				}
			}
		}

		return res;
	}

	void del(i64 x) {
		int p = 0;
		for (i64 i = 29; i >= 0; -- i) {
			i64 s = x >> i & 1;
			p = Tr[p][s];
			-- cnt[p];
		}
	}
};

void solve() {
	i64 l, r;
	std::cin >> l >> r;
	int n = r - l + 1;
	i64 ans = 0;
	Trie tr;
	for (i64 i = l; i <= r; ++ i) {
		tr.insert(i);
	}

	std::vector<i64> a(n + 1, -1);
	while (1) {
		std::vector<i64> p(n + 1, -1);
		bool flag = false;
		for (i64 i = l; i <= r; ++ i) {
			if (a[i - l] == -1) {
				p[i - l] = tr.query(i);
				flag = true;
			}
		}
		
		if (!flag) {
			break;
		}

		for (i64 i = l; i <= r; ++ i) {
			if (p[i - l] == -1) {
				continue;
			}

			if (i == p[p[i - l] - l]) {
				a[i - l] = p[i - l];
				tr.del(i);
			}
		}

	}

	for (i64 i = l; i <= r; ++ i) {
		ans += a[i - l] | i;
	}
	std::cout << ans << "\n";
	for (i64 i = l; i <= r; ++ i) {
		std::cout << a[i - l] << " \n"[i == r];
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

E. Yet Another MEX Problem

题意:一个数组的值为大于这个数组的\(mex\)的数的个数。给你一个数组\(a\),求对于每一个\(i \in [1, n]\),以\(i\)结尾的子数组的最大值。

一个数组的值为\(\sum_{i=1}^{n} [a_i > mex]\)。可以改写为\(\max_{j \notin a} \sum_{i=1}^{n} [a_i > j]\)
那么\(i\)的答案为\(\max_{l=1}^{i} \max_{j \notin a[l..i]} \sum_{k=l}^{i} [a_k > j]\),交换一下顺序:\(\max_{j=0}^{n} \max_{l: j \notin a[l...i]} \sum_{k=l}^{i} [a_k > j]\)
那么我们可以用线段树维护每个\(j\)的最大值。
假设已经处理了\([1, i - 1]\),现在加入\(a_i\),那么对于\(j \in [0, a_i - 1]\)\(a_i\)产生了\(1\)的贡献,于是\([0, a_i - 1]\)都加一。然后对于\(j = a_i\),发现没有合适的\(l\)可以取,所有\(j = a_i\)时值变为\(0\)。对于\(j \in [a_i + 1, n]\),不产生影响。于是用线段树维护区间最大值就行。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

template <class Info, class Tag>
struct LazySegmentTree {
	struct Node {
		int l, r;
		Info info;
		Tag tag;
	};

	std::vector<Node> tr;
	LazySegmentTree() {}
	LazySegmentTree(int n) {
		init(n);
	}

	void init(int n) {
		tr.assign(n << 2, {});
		build(0, n - 1);
	}

	void pushdown(Node & u, const Tag & tag) {
		u.info = u.info + tag;
		u.tag = u.tag + tag;
	}

	void pushdown(int u) {
		if (tr[u].tag.exist()) {
			pushdown(tr[u << 1], tr[u].tag);
			pushdown(tr[u << 1 | 1], tr[u].tag);
			tr[u].tag.clear();
		}
	}

	void pushup(int u) {
		tr[u].info = tr[u << 1].info + tr[u << 1 | 1].info;
	}

	void build(int l, int r, int u = 1) {
		tr[u] = {l, r, {}};
		if (l == r) {

			return;
		}

		int mid = l + r >> 1;
		build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
		pushup(u);
	}

	void modify(int l, int r, const Tag & tag, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			pushdown(tr[u], tag);
			return;
		}

		pushdown(u);
		int mid = tr[u].l + tr[u].r >> 1;
		if (l <= mid) {
			modify(l, r, tag, u << 1);
		}

		if (r > mid) {
			modify(l, r, tag, u << 1 | 1);
		}

		pushup(u);
	}

	void modify(int p, const Info & info) {
		int u = 1;
		while (tr[u].l != tr[u].r) {
			int mid = tr[u].l + tr[u].r >> 1;
			pushdown(u);
			if (p <= mid) {
				u = u << 1;
			} else {
				u = u << 1 | 1;
			}
		}

		tr[u].info = info;
		u >>= 1;
		while (u) {
			pushup(u);
			u >>= 1;
		}
	}

	Info query(int l, int r, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			return tr[u].info;
		}

		pushdown(u);
		int mid = tr[u].l + tr[u].r >> 1;
		if (r <= mid) {
			return query(l, r, u << 1);
		} else if (l > mid) {
			return query(l, r, u << 1 | 1);
		}

		return query(l, r, u << 1) + query(l, r, u << 1 | 1);
	}
};

struct Info {
	int max;
};

struct Tag {
	int add;
	bool exist() {
		return add != 0;
	}

	void clear() {
		add = 0;
	}
};

Info operator + (const Info & a, const Info & b) {
	Info res{};
	res.max = std::max(a.max, b.max);
	return res;
}

Info operator + (const Info & a, const Tag & b) {
	Info res{};
	res.max = a.max + b.add;
	return res;
}

Tag operator + (const Tag & a, const Tag & b) {
	Tag res{};
	res.add = a.add + b.add;
	return res;
}

void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	LazySegmentTree<Info, Tag> tr(n + 1);
	std::vector<int> ans(n);
	for (int i = 0; i < n; ++ i) {
		tr.modify(0, a[i], Tag{1});
		tr.modify(a[i], Info{0});
		ans[i] = tr.query(0, n).max;
	}

	for (int i = 0; i < n; ++ i) {
		std::cout << ans[i] << " \n"[i == n - 1];
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
posted @ 2025-09-22 01:13  maburb  阅读(743)  评论(0)    收藏  举报