题解 CF1539F Strange Array
赛场上顺利地做完前面四题就来看 F 了,但是一直没有什么思路,现在发现还是很可做的。
题目里要求的“和中心的距离”这个条件不太好求,因为要中心就得知道区间长度,而数据范围有 \(2 \times 10^5\), 不支持枚举长度后再干什么了,所以肯定要经过适当的转化。
如果该数在区间中心的右边,那么它离中心的距离应该是区间内小于等于它的个数减去大于等于它的个数再除以二,这点用 \(cntl, cntr, cnte\) 表示比其小的/大的/相等的就可以得到。
数在左边同样可以得到一个式子。
那么这个东西怎么处理呢?一段区间以内求比其小的大的似乎是需要树套树的,而且要求这样一个和的最大值也不好做。
先解决一下比一个数小的问题。权值线段树就算了,可以直接将数排个序,然后处理每个之前将把比它大的标记掉就行了。所以只剩下区间的问题。
这个区间上一些个数的差可以按套路把后面减的标记为 \(-1\), 然后就只需要求跨过这个点的所有区间中和最大的。可以用线段树来解决。具体地,若我们要跨过的点在 \(x\), 那么就是要求 \(1\to x-1\) 中最大的后缀和 \(x+1 \to n\) 中最大的前缀的和。用线段树维护区间最大前缀和后缀就可以了,写成结构体可能会方便一些。
一些细节看代码吧。
#include <cstdio>
#include <vector>
const int N = 200005;
int n, ans[N], x;
struct twt {
int sum, pre, suf;
twt(int x = 0, int y = 0, int z = 0) { sum = x, pre = y, suf = z; }
friend twt operator+(twt a, twt b) {
return twt(a.sum + b.sum, std::max(a.pre, b.pre + a.sum),
std::max(b.suf, a.suf + b.sum));
}
} t[N << 2];
std::vector<int> pos[N];
void Update(int p, int l, int r, int x, int y) {
if (l == r) {
t[p].pre = std::max(0, y), t[p].suf = std::max(0, y);
t[p].sum = y;
return;
}
int mid = l + (r - l) / 2;
if (x <= mid)
Update(p + p, l, mid, x, y);
else
Update(p + p + 1, mid + 1, r, x, y);
t[p] = t[p + p] + t[p + p + 1];
}
twt Query(int p, int l, int r, int x, int y) {
if (x > y) return twt(0, 0, 0);
if (l == x && r == y) return t[p];
int mid = l + (r - l) / 2;
if (y <= mid)
return Query(p + p, l, mid, x, y);
else if (x > mid)
return Query(p + p + 1, mid + 1, r, x, y);
else
return Query(p + p, l, mid, x, mid) +
Query(p + p + 1, mid + 1, r, mid + 1, y);
}
void solve1() {
for (int i = 1; i <= n; i++) Update(1, 1, n, i, 1);
for (int i = 1; i <= n; i++) {
for (int j = 0; j < (signed)pos[i].size(); j++) {
int v = pos[i][j];
int an =
Query(1, 1, n, 1, v - 1).suf + 1 + Query(1, 1, n, v + 1, n).pre;
ans[v] = std::max(ans[v], an / 2);
}
for (int j = 0; j < (signed)pos[i].size(); j++)
Update(1, 1, n, pos[i][j], -1);
}
}
void solve2() {
for (int i = 1; i <= n; i++) Update(1, 1, n, i, 1);
for (int i = n; i >= 1; i--) {
for (int j = 0; j < (signed)pos[i].size(); j++) {
int v = pos[i][j];
int an =
Query(1, 1, n, 1, v - 1).suf + 1 + Query(1, 1, n, v + 1, n).pre;
ans[v] = std::max(ans[v], (an - 1) / 2);
}
for (int j = 0; j < (signed)pos[i].size(); j++)
Update(1, 1, n, pos[i][j], -1);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &x), pos[x].push_back(i);
solve1(), solve2();
for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
return 0;
}