Codeforces Round 176

A

奇数 \(x\) 最多被操作一次,所以先判 \(n\) 是否为奇数,然后规约成偶数即可。

B

\(k \ge 2\) 时,可以选择任意一个长度为 \(k + 1\) 的子序列:先选首尾,然后再选中间的 \(k - 2\) 个,最后一个一定在间隔之中,可以取到。答案为最大的 \(k+1\) 个数之和。

\(k=1\) 时,扫一遍数组就行了。

C

考虑两个颜色 \(i, j\) ( \(i \ne j\) ) 的方案数:

  • \(a_i + a_j < n\),答案为 \(0\)
  • 否则答案为 \(\min(n - 1, a_i + a_j - n + 1)\)

于是先把 \(a_i\) 排序,然后对于第二种情况二分出取 \(\min\) 的分界点即可。

D

\(x = y\) 的条件是 \(x\) 的前若干位和 \(y\) 的前若干位相等,那么直接枚举是哪个前缀,预处理一个简单背包即可。

E

答案可以分成几种情况:

  • \(a_i\) 只有一种取值,\(b_i\) 只有一种取值,贡献是 \((A + 1)(B + 1)\)
  • \(a_i\) 有两种取值,\(b_i\) 只有一种取值,贡献是 \((B + 1)\dfrac{A(A + 1)}{2}2^n\)
  • \(a_i\) 只有一种取值,\(b_i\) 有两种取值,贡献是 \((A + 1)\dfrac{B(B + 1)}{2}2^m\)
  • \(a_i\) 有两种取值,\(b_i\) 有两种取值,假设 \(a\) 中取值为 \(x, y\)\(b\) 中取值为 \(p, q\),那么要满足:\(x \oplus p = y \oplus q\),移项得 \(x \oplus y = p \oplus q\)
    于是数位 dp 出有多少合法的 \((x, y, p, q)\) 即可。

F

子序列合法的充要条件是 \(a_1, a_n\) 分别为全局严格最小和最大值。

那么可以转化成:有 \(n\) 个点 \((i, a_i)\),求出 \(i, j\) ( \(i < j \land a_i < a_j\) ) 使得点 \((i, a_i + 1)\)\((j, a_j - 1)\) 矩形中框住的点最多。

对于 \(i\),如果它的左下角有点,那么它作为矩形的左下角一定不优,同理,对于 \(j\),如果它右上角有点,那么它作为矩形右上角一定不优。

所以我们可以分离出两个下降的点对序列 \(l, r\),也就是序列的前缀最小值和后缀最大值。

枚举 \(l\) 中的每个点作为矩形左下角的答案,用线段树维护 \(r\) 中每个点作为矩形右上角的答案,考虑由 \(l_i\)\(l_{i + 1}\) 有哪些点的贡献会有变化:

  • \(l_i < j \le l_{i + 1}\),那么 \(j\) 的贡献应当减去。
  • \(a_{l_i} \ge a_j > a_{l_{i + 1}}\),那么 \(j\) 的贡献应当加上。

并且每个 \(j\) 都是对 \(r\) 中的一段区间产生贡献,可以二分出这个区间然后线段树区间加。贡献计算完毕后求线段树全局 \(\max\) 即可。

代码:

//seg.add(l, r, v) : 将区间 [l, r) 加上 v。
//seg.query(l, r) : 查询区间 [l, r) 的最大值。

void solve() {
	cin >> n;

	bool dec = 1;
	for (int i = 0; i < n; ++i) {
		cin >> a[i];
		if (i > 0 && a[i - 1] < a[i]) {
			dec = 0;
		}
	}
	if (dec) {
		cout << 1 << "\n";
		return ;
	}

	vector<int> l, r;
	for (int i = 0; i < n; ++i) {
		if (l.empty() || a[i] < a[l.back()]) {
			l.push_back(i);
		}
	}
	for (int i = n - 1; i >= 0; --i) {
		if (r.empty() || a[i] > a[r.back()]) {
			r.push_back(i);
		}
	}

	for (int i = 0; i < r.size(); ++i) {
		seg.modify(i, 0);
	}

	vector<int> ord(n);
	iota(ord.begin(), ord.end(), 0);
	sort(ord.begin(), ord.end(), [&](int x, int y) {
		return a[x] > a[y];
	});

	auto add = [&](int i, int v) {
		int p = upper_bound(r.begin(), r.end(), i, [&](int x, int y) {
			return a[x] < a[y];
		}) - r.begin();
		int q = lower_bound(r.begin(), r.end(), i, greater()) - r.begin();
		seg.add(p, q, v);
	} ;

	int ans = 0;
	for (int i = 0, ptr = 0; i < l.size(); ++i) {
		while (ptr < n && a[ord[ptr]] > a[l[i]]) {
			if (ord[ptr] > l[i]) {
				add(ord[ptr], 1);
			}
			++ptr;
		}
		ans = max(ans, seg.query(0, r.size()).max);
		if (i + 1 < l.size()) {
			for (int j = l[i]; j < l[i + 1]; ++j) {
				if (a[j] > a[l[i]]) {
					add(j, -1);
				}
			}
		}
	}

	cout << ans + 2 << "\n";
}
posted @ 2025-03-20 19:01  CTHOOH  阅读(12)  评论(0)    收藏  举报