CF2126G2 思路分享(单调栈,主席树)
https://codeforces.com/contest/2126/problem/G2
题意
给定长为 \(n\) 的序列 \(a\),求 \(a\) 的子数组中最大的 \(med(a[l\cdots r])-min(a[l\cdots r])\).
其中 \(med(a)\) 表示将 \(a\) 升序排序后第 \(\left\lceil \frac{1+|a|}{2} \right\rceil\) 个元素.
\(1\le n \le 2\cdot 10^5\),\(1\le a_i \le n\).
思路
考虑单调栈,求出每个元素的最小值支配区间,固定最小值.
然后考虑二分中位数 \(mid\),经典地,令 \(a_i \ge mid\) 的 \(a_i\) 为 \(1\),其他为 \(-1\),问题转变成判断支配区间内是否存在包含 \(i\) 的某个区间和 \(\ge 0\).
对于单个 \(mid\),区间和可以由 \((left_i,i]\) 的后缀最大值与 \([i,right_i)\) 的前缀最大值之和表示,考虑用线段树维护.
我们发现,当 \(mid\) 增加的时候,某些位置由 \(1\) 变成 \(-1\),相当于线段树的单点更新,总共更新的次数不会超过 \(n\),考虑用主席树维护所有版本的线段树.
当 \(mid+1\) 时,可能有多个位置发生更新,记录最后一个更新的线段树版本.
时间复杂度 \(\mathcal{O}(n\log^2 n)\).
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int INF = 1e9;
class node{
public:
int premx=-INF/2,sufmx=-INF/2;
int sum = 0;
int left=-1,right=-1;
};
void solve(){
int n;
cin >> n;
vector<int> a(n+1);
for (int i=1;i<=n;i++){
cin >> a[i];
}
vector<int> left(n+1),right(n+1);
stack<int> stk;
for (int i=1;i<=n;i++){
while (!stk.empty() && a[i]<=a[stk.top()]){
stk.pop();
}
left[i] = stk.empty()?0:stk.top();
stk.push(i);
}
while (!stk.empty()){
stk.pop();
}
for (int i=n;i>=1;i--){
while (!stk.empty() && a[i]<a[stk.top()]){
stk.pop();
}
right[i] = stk.empty()?n+1:stk.top();
stk.push(i);
}
vector<vector<int>> idx(n+1);
for (int i=1;i<=n;i++){
idx[a[i]].push_back(i);
}
vector<node> seg;
vector<int> root;
vector<int> last(n+1);
function<int(int,int)> build = [&](int l,int r)->int{
if (l==r){
node temp;
temp.sum = temp.premx = temp.sufmx = 1;
seg.push_back(temp);
return seg.size()-1;
}
int mid = l+r >> 1;
seg.emplace_back();
int rt = seg.size()-1;
seg[rt].left = build(l,mid);
seg[rt].right = build(mid+1,r);
seg[rt].sum = seg[seg[rt].left].sum+seg[seg[rt].right].sum;
seg[rt].premx = max(seg[seg[rt].left].premx,seg[seg[rt].left].sum+seg[seg[rt].right].premx);
seg[rt].sufmx = max(seg[seg[rt].right].sufmx,seg[seg[rt].left].sufmx+seg[seg[rt].right].sum);
return rt;
};
root.push_back(build(1,n));
last[1] = root.back();
function<int(int,int,int,int)> update = [&](int rt,int l,int r,int pos){
if (l==r){
seg.emplace_back();
int nrt = seg.size()-1;
seg[nrt].sum = seg[nrt].premx = seg[nrt].sufmx = -1;
return nrt;
}
int mid = l+r >> 1;
seg.emplace_back(seg[rt]);
int nrt = seg.size()-1;
if (pos<=mid){
seg[nrt].left = update(seg[rt].left,l,mid,pos);
}
else{
seg[nrt].right = update(seg[rt].right,mid+1,r,pos);
}
seg[nrt].sum = seg[seg[nrt].left].sum+seg[seg[nrt].right].sum;
seg[nrt].premx = max(seg[seg[nrt].left].premx,seg[seg[nrt].left].sum+seg[seg[nrt].right].premx);
seg[nrt].sufmx = max(seg[seg[nrt].right].sufmx,seg[seg[nrt].left].sufmx+seg[seg[nrt].right].sum);
return nrt;
};
for (int mid=2;mid<=n;mid++){
for (auto& i:idx[mid-1]){
root.push_back(update(root.back(),1,n,i));
}
last[mid] = root.back();
}
function<node(int,int,int,int,int)> query_range = [&](int rt,int l,int r,int x,int y){
if (r<x || l>y) return node();
if (x<=l && r<=y){
return seg[rt];
}
int mid = l+r >> 1;
node left = query_range(seg[rt].left,l,mid,x,y);
node right = query_range(seg[rt].right,mid+1,r,x,y);
node temp;
temp.sum = left.sum+right.sum;
temp.premx = max(left.premx,left.sum+right.premx);
temp.sufmx = max(right.sufmx,left.sufmx+right.sum);
return temp;
};
int res = -INF;
for (int i=1;i<=n;i++){
auto check = [&](int mid){
auto L = query_range(last[mid],1,n,left[i]+1,i);
auto R = query_range(last[mid],1,n,i,right[i]-1);
int cur = L.sufmx+R.premx-(a[i]>=mid?1:-1);
return cur>=0;
};
int l=1,r=n;
while (l<=r){
int mid = l+r >> 1;
if (check(mid)){
l = mid+1;
}
else{
r = mid-1;
}
}
res = max(res,r-a[i]);
}
cout << res << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号