Codeforces Round 1052 (Div. 2) A~E
A - Equal Occurrences
枚举。
枚举 \(1\sim n\) 的高度,把满足条件的加起来取最大值即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i += 1) {
int x;
cin >> x;
a[x] += 1;
}
int ans = 0;
for (int i = 1; i <= n; i += 1) {
int res = 0;
for (int j = 1; j <= n; j += 1) {
if (a[j] >= i) {
res += i;
}
}
ans = max(ans, res);
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
B - Merging the Sets
思维。
先把所有集合里的数出现次数记录一下,出现次数为 \(1\) 的,说明那个集合是必须要有的,记 \(need\) 为必须要有的集合个数,判断最后 \(n\) 是否大于 \(need + 1\) 即可。
只要至少有两个不是必须的,就可以组合出 \(00,01,10,11\) 四种不同的集合了。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n, m;
cin >> n >> m;
vector<vector<int>> ve(n);
vector<int> cnt(m + 1);
for (int i = 0; i < n; i += 1) {
int k;
cin >> k;
for (int j = 0; j < k; j += 1) {
int x;
cin >> x;
ve[i].push_back(x);
cnt[x] += 1;
}
}
if(count(cnt.begin() + 1, cnt.end(), 0) > 0){
cout << "NO\n";
return;
}
int need = 0;
for(auto &x : ve){
for(auto &v : x){
if(cnt[v] == 1){
need += 1;
break;
}
}
}
if(n > need + 1){
cout << "YES\n";
return;
}
cout << "NO\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
C - Wrong Binary Search
构造。
通过打表或者手玩或者分析可以发现的是,为 \(1\) 的位置,它的左边不能有大于 \(i\) 的数,同理右边不能有小于 \(i\) 的数。
所以 \(i\) 的位置必须先放 \(i\) ,然后对于两个 \(1\) 中间,可以偏移 \(1\) 的顺序放,类似 \(n, 1, 2... n - 1\),最后再检查一遍有没有为 \(0\) 的位置放了等于 \(i\) 的即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int n;
cin >> n;
string s;
cin >> s;
s = " " + s;
vector<int> p(n + 1);
int lst = 1;
for (int i = 1; i <= n; i += 1) {
if (s[i] == '1') {
p[i] = i;
if (lst < i) {
p[lst] = i - 1;
for (int j = lst + 1; j < i; j += 1) {
p[j] = j - 1;
}
}
lst = i + 1;
}
}
if (lst <= n) {
p[lst] = n;
for (int i = lst + 1; i <= n; i += 1) {
p[i] = i - 1;
}
}
for (int i = 1; i <= n; i += 1) {
if (s[i] == '0' && p[i] == i) {
cout << "NO\n";
return;
}
}
cout << "YES\n";
for (int i = 1; i <= n; i += 1) {
cout << p[i] << " \n"[i == n];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D1 - Max Sum OR (Easy Version)
思维,位运算。
由于 \(0\sim r\) 都有,所以对于一个数总是有一个它最高位往后为 \(0\) 的位置置 \(1\) 的数相对应,比如 \(10100\) 和 \(01011\) 这样。

所以直接将最高位及后面全取 \(1\) 后与该数异或一下就行了。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int l, r;
cin >> l >> r;
int n = r;
i64 ans = 0;
vector<int> a(n + 1);
for(int i = r;i >= 0;i -= 1){
if(a[i]){
ans += a[i] | i;
continue;
}
int x = 0;
for(int j = 31;j >= 0;j -= 1){
if(i >> j & 1){
x = (1 << j + 1) - 1;
break;
}
}
x ^= i;
a[x] = i;
a[i] = x;
ans += x | i;
}
cout << ans << "\n";
for(int i = 0;i <= n;i += 1){
cout << a[i] << " \n"[i == n];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D2 - Max Sum OR (Hard Version)
思维,位运算。
其实原理和上面很类似,但奈何赛时居然没反应过来
由上图可以看出,其实从按位来看,以 \(1,0\) 的分界线作为对称轴,两边对称的数就是互相对应的,所以可以从高到低每一位根据 \(0/1\) 的个数决定少的那一边去对称,多的那一边继续枚举下一位去对称。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve() {
int l, r;
cin >> l >> r;
int n = r - l + 1;
vector<int> a(n);
iota(a.begin(), a.end(), l);
int L = 0, R = n;
for (int i = 30; i >= 0; i--) {
int x = L;
while (x < R && (~a[x] >> i & 1)) {
x++;
}
if (x - L < R - x) {
int m = 2 * x - L;
reverse(a.begin() + L, a.begin() + m);
L = m;
}
else {
int m = 2 * x - R;
reverse(a.begin() + m, a.begin() + R);
R = m;
}
}
i64 ans = 0;
for(int i = 0;i < n;i += 1){
ans += a[i] | (i + l);
}
cout << ans << "\n";
for(int i = 0;i < n;i += 1){
cout << a[i] << " \n"[i + 1 == n];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
E - Yet Another MEX 问题
线段树。
对于数组 \(a\) 和一个不出现在数组 \(a\) 中的整数 \(x\),设 \(g(a, x)\) 表示为 \(a\) 中大于 \(x\) 的元素个数。显然有 \(g(a, x) \le g(a,\text{mex}(a))\),所以当 \(x = \text{mex}(a)\) 时能最大化答案。
对于固定的右端点 \(r\),枚举 \(x\) 并最大化 \(g(a[l...r], x)\),同时保持 \(a[l...r]\) 不包含 \(x\),显然当 \(l = lst_x + 1\) 最优,\(lst_x\) 表示 \(x\) 最后一次出现的位置。
那么答案就是 \(\max_{x=0}^{n}g(a,x)\)。
当加入 \(a_i\) 时,对所有 \([1,a_i-1]\) 都可以增加 \(1\) 的贡献,而 \(x = a_i\) 包含了 \(x\),不符合之前的定义,置为 \(0\),对于 \([a_i+1,n]\),不产生贡献。
考虑用线段树维护 \(g(a, x)\) 即可,由于 \(x\) 可以为 \(0\),一开始可以都偏移 \(1\) 位。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
template<class Node>
struct SegmentTree {
#define lc u<<1
#define rc u<<1|1
const int n, N;
vector<Node> tr;
SegmentTree(): n(0) {}
SegmentTree(int n_): n(n_), N(n * 4 + 10) {
tr.reserve(N);
tr.resize(N);
}
SegmentTree(vector<int> init) : SegmentTree(init.size() - 1) {
function<void(int, int, int)> build = [&](int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
init_lazy(tr[u]);
if (l == r) {
tr[u] = {l, r, 0, init[l]};
return ;
}
i64 mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(tr[u], tr[lc], tr[rc]);
};
build(1, 1, n);
}
void cal_lazy(Node & fa, Node & ch) {
i64 b = fa.add;
ch.Max += b;
}
void tag_union(Node& fa, Node& ch) {
i64 b = fa.add;
ch.add += b;
}
void init_lazy(Node& u) {
u.add = 0;
}
void pushdown(i64 u) {
if (tr[u].add != 0) {
cal_lazy(tr[u], tr[lc]);
cal_lazy(tr[u], tr[rc]);
tag_union(tr[u], tr[lc]);
tag_union(tr[u], tr[rc]);
init_lazy(tr[u]);
}
}
void pushup(Node& U, Node& L, Node& R) { //上传
U.l = L.l,U.r = R.r;
U.Max = max(L.Max, R.Max);
}
void modify(int u, int l, int r, int k) {
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].add += k;
tr[u].Max += k;
return ;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid) {
modify(lc, l, r, k);
}
if (r > mid) {
modify(rc, l, r, k);
}
pushup(tr[u], tr[lc], tr[rc]);
}
void modify(int u, int pos, int k) {
if (tr[u].l >= pos && tr[u].r <= pos) {
tr[u].Max = k;
return ;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (pos <= mid) {
modify(lc, pos, k);
}
if (pos > mid) {
modify(rc, pos, k);
}
pushup(tr[u], tr[lc], tr[rc]);
}
Node query(int u, int l, int r) { //区查
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u];
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if (r <= mid) {
return query(lc, l, r);
}
if (l > mid) {
return query(rc, l, r);
}
Node U;
Node L = query(lc, l, r), R = query(rc, l, r);
pushup(U, L, R);
return U;
}
};
struct Node { //线段树定义
int l, r, add;
i64 Max;
};
void solve() {
int n;
cin >> n;
vector<int> a(n + 2);
SegmentTree<Node> S(a);
for (int i = 1; i <= n; i += 1) {
cin >> a[i];
a[i] += 1;
}
for (int i = 1; i <= n; i += 1) {
S.modify(1, 1, a[i], 1);
S.modify(1, a[i], 0);
cout << S.query(1, 1, n + 1).Max << " \n"[i == n];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号