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

浙公网安备 33010602011771号