题目
给定数组\(a\),对于某个元素\(a_i\)和区间\([l,r]\)满足\(l\le i \le r\)。如果将\(a_l,a_{l+1},...a_r\)排序后,原\(a_i\)在排序后的新位置为\(j\)(值相同是可以任意排序),那么\(a_i\)的奇异值为\(|j-\lfloor\frac{l+r+1}{2}\rfloor|\)。问每个元素最大的奇异值是多少。
题解
假设对于某个元素\(a_i\)和区间\([l,r]\)满足\(l\le i \le r\),如果排完序后\(a_i\)在\(\lfloor\frac{l+r+1}{2}\rfloor\)的左侧,那么它的排位应该尽可能地靠左;否则要尽可能的靠右。因此可以算两遍,假设\(a_i\)在左侧算一次结果,在右侧算一次结果,两次取最大值,这样就可以去掉绝对值。
假设\(a_i\)在左侧,因为\(a_i\)的排位要尽可能地小,所以它的排位就是区间\([l,r]\)中严格小于\(a_i\)的值的个数加1。这个个数可以通过维护前缀和快速计算。那么奇异值为\(d=\lfloor\frac{r-(l-1)}{2}+1\rfloor-(pre[r]-pre[l-1]+1)\)。整理后可得
\[d=\lfloor \frac{(r-2pre[r]) - (l-2pre[l])}{2}\rfloor \]所以要使\(d\)最大,只需使\((r-2pre[r]) - (l-2pre[l])\)最大,直接用线段树维护这个值,然后区间询问左部分的最小值和右部分的最大值即可。
同理假设\(a_i\)在右侧,因为\(a_i\)的排位要尽可能地大,所以它的排位就是区间\([l,r]\)中小于等于\(a_i\)的值的个数,奇异值为\(d=(pre[r]-pre[l-1])-\lfloor\frac{r-(l-1)}{2}+1\rfloor\),整理后得
\[d=\lceil \frac{(r-2pre[r])-(l-2pre[r])-2}{2}\rceil \]至于如何维护这个前缀和,只需按照值从小到大更新计算即可。
#include <bits/stdc++.h>
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 2e5 + 10;
const double eps = 1e-5;
int mi[N << 2];
int mx[N << 2];
int lazy[N << 2];
void init(int l, int r, int rt) {
lazy[rt] = mi[rt] = mx[rt] = 0;
if(l == r) return ;
int mid = (l + r) / 2;
init(l, mid, rt << 1);
init(mid + 1, r, rt << 1 | 1);
}
void pushdown(int rt) {
if(lazy[rt]) {
lazy[rt << 1] += lazy[rt];
lazy[rt << 1 | 1] += lazy[rt];
mi[rt << 1] += lazy[rt];
mi[rt << 1 | 1] += lazy[rt];
mx[rt << 1] += lazy[rt];
mx[rt << 1 | 1] += lazy[rt];
lazy[rt] = 0;
}
}
void upd(int l, int r, int L, int R, int val, int rt) {
if(l >= L && r <= R) {
mi[rt] += val;
mx[rt] += val;
lazy[rt] += val;
return ;
}
pushdown(rt);
int mid = (l + r) / 2;
if(L <= mid) upd(l, mid, L, R, val, rt << 1);
if(R > mid) upd(mid + 1, r, L, R, val, rt << 1 | 1);
mi[rt] = min(mi[rt << 1], mi[rt << 1 | 1]);
mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]);
}
int quemi(int l, int r, int L, int R, int rt) {
if(l >= L && r <= R) {
return min(0, mi[rt]);
}
int res = 0;
pushdown(rt);
int mid = (l + r) / 2;
if(L <= mid) res = min(res, quemi(l, mid, L, R, rt << 1));
if(R > mid) res = min(res, quemi(mid + 1, r, L, R, rt << 1 | 1));
mi[rt] = min(mi[rt << 1], mi[rt << 1 | 1]);
return res;
}
int quemx(int l, int r, int L, int R, int rt) {
if(l >= L && r <= R) {
return mx[rt];
}
int res = -INF;
pushdown(rt);
int mid = (l + r) / 2;
if(L <= mid) res = max(res, quemx(l, mid, L, R, rt << 1));
if(R > mid) res = max(res, quemx(mid + 1, r, L, R, rt << 1 | 1));
mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]);
return res;
}
int ans[N];
vector<int> pos[N];
int main() {
IOS;
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
int x;
cin >> x;
pos[x].push_back(i);
upd(1, n, i, i, i, 1);
}
for(int i = 1; i < N; i++) {
for(int p : pos[i]) {
int d = quemx(1, n, p, n, 1) - quemi(1, n, 1, p, 1);
if(d < 0) continue;
d = d / 2;
ans[p] = max(ans[p], d);
}
for(int p : pos[i]) {
upd(1, n, p, n, -2, 1);
}
}
init(1, n, 1);
for(int i = 1; i <= n; i++) {
upd(1, n, i, i, -i, 1);
}
for(int i = 1; i < N; i++) {
for(int p : pos[i]) {
upd(1, n, p, n, 2, 1);
}
for(int p : pos[i]) {
int d = quemx(1, n, p, n, 1) - quemi(1, n, 1, p, 1) - 2;
if(d < 0) continue;
d = d / 2 + (d % 2 != 0);
ans[p] = max(ans[p], d);
}
}
for(int i = 1; i <= n; i++) cout << ans[i] << " \n"[i == n];
}