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;
}
posted @ 2025-09-22 20:22  Ke_scholar  阅读(55)  评论(0)    收藏  举报