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;
}

浙公网安备 33010602011771号