# CF2126G2 Big Wins! (hard version)
## 题目描述
这是该问题的困难版本。不同之处在于本版本中 $a_i \leq n$。
给定一个长度为 $n$ 的整数数组 $a_1, a_2, \dots, a_n$。
你的任务是找到一个子数组 $a[l, r]$(即一段连续的元素 $a_l, a_{l + 1}, \dots, a_r$),使得表达式 $\text{med}(a[l, r]) - \min(a[l, r])$ 的值最大。
其中:
- $\text{med}$ 表示子数组的中位数,即将子数组排序后,第 $\left\lceil \frac{k + 1}{2} \right\rceil$ 个元素,$k$ 为子数组长度;
- $\min$ 表示该子数组中的最小元素。
例如,考虑数组 $a=[1, 4, 1, 5, 3, 3]$,选择子数组 $a[2, 5] = [4, 1, 5, 3]$。排序后为 $[1, 3, 4, 5]$。
- $\text{med}(a[2, 5]) = 4$,因为 $\left\lceil \frac{4 + 1}{2} \right\rceil = 3$,排序后第 3 个元素为 $4$;
- $\min(a[2, 5]) = 1$,因为最小元素为 $1$。
在本例中,$\text{med} - \min = 4 - 1 = 3$。
## 输入格式
第一行包含一个整数 $t$($1 \le t \le 10^4$),表示测试用例的数量。
每个测试用例的第一行包含一个整数 $n$($1 \leq n \leq 2 \cdot 10^5$),表示数组的长度。
每个测试用例的第二行包含 $n$ 个整数 $a_1, a_2, \dots, a_n$($1 \leq a_i \leq n$),表示数组的元素。
保证所有测试用例中 $n$ 的总和不超过 $2 \cdot 10^5$。
## 输出格式
对于每个测试用例,输出一个整数,表示所有子数组中 $\text{med} - \min$ 的最大可能值。
## 输入输出样例 #1
### 输入 #1
```
5
5
3 2 5 3 1
4
4 1 1 3
7
6 1 3 4 6 2 7
4
4 2 3 1
5
1 2 3 4 5
```
### 输出 #1
```
3
3
5
2
2
```
## 说明/提示
在第一个示例中,考虑数组 $a=[3,\ 2,\ 5,\ 3,\ 1]$,可以选择子数组 $a[2,\ 3]$,即元素 $[2,\ 5]$。
- 子数组长度为 $2$。
- 中位数为排序后第 $\left\lceil \dfrac{3}{2} \right\rceil = 2$ 个元素。排序后为 $[2,\ 5]$,$\text{med} = 5$。
- 子数组的最小元素为 $2$。
因此,$\text{med} - \min = 5 - 2 = 3$,这是最大答案。
在第二个测试用例中,数组 $a=[4,\ 1,\ 1,\ 3]$,可以选择子数组 $a[1,\ 2]$,即元素 $[4,\ 1]$。
- 子数组长度为 $2$。
- 中位数为排序后第 $\left\lceil \dfrac{3}{2} \right\rceil = 2$ 个元素。排序后为 $[1,\ 4]$,$\text{med} = 4$。
- 子数组的最小元素为 $1$。
因此,$\text{med} - \min = 4 - 1 = 3$。
可以证明,这两个子数组都是最优的,能够得到表达式 $\text{med} - \min$ 的最大值。
由 ChatGPT 4.1 翻译
//https://blog.youkuaiyun.com/ez_gsn/article/details/124840464?ops_request_misc=elastic_search_misc&request_id=743433b74cd2da2925edc4e1cc7af925&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~ElasticSearch~search_v2-1-124840464-null-null.142^v102^pc_search_result_base9&utm_term=P2839%20%5B%E5%9B%BD%E5%AE%B6%E9%9B%86%E8%AE%AD%E9%98%9F%5D%20middle
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int a[20005];
int b[20005];
int sl;
vector<int> pos[20005];
int cnt;
int rt[20005];
struct TREE {
int l,r;
int sum;//区间和
int lml,rml;//最大前缀和,最大后缀和
} tr[20005*40];
void pushup(int x) {
tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum;
tr[x].lml=max(tr[tr[x].l].lml,tr[tr[x].l].sum+tr[tr[x].r].lml);
tr[x].rml=max(tr[tr[x].r].rml,tr[tr[x].r].sum+tr[tr[x].l].rml);
}
int build(int i,int l,int r) {
i=++cnt;
if(l==r) {
tr[i].sum=tr[i].lml=tr[i].rml=1;
//初始线段树对应最小中位数候选值(mid = num[1]),
//此时所有元素都 ≥ mid,因此每个位置都是 1。
return i;
}
int mid=(l+r)/2;
tr[i].l=build(tr[i].l,l,mid);
tr[i].r=build(tr[i].r,mid+1,r);
pushup(i);
return i;
}
int update(int x,int y,int ps,int l,int r) { //单点修改
//f:是否需要新建节点 0:需要 1:不需要
//x:当前新版本的节点索引(需修改的节点)
//y:旧版本的对应节点索引(数据来源)
x=++cnt;
if(l==r) {
tr[x].lml=tr[x].rml=tr[x].sum=1;
return x;
}
tr[x]=tr[y];
int mid=(l+r)/2;
if(ps<=mid) tr[x].l=update(tr[x].l,tr[y].l,ps,l,mid);
else tr[x].r=update(tr[x].r,tr[y].r,ps,mid+1,r);
pushup(x);
return x;
}
int query_sum(int i,int l,int r,int qry_l,int qry_r) { //区间和
if(i==0) return 0;
if(l>=qry_l&&r<=qry_r) return tr[i].sum;
int ans=0,mid=(l+r)/2;
if(mid>=qry_l) ans+=query_sum(tr[i].l,l,mid,qry_l,qry_r);
if(mid<qry_r) ans+=query_sum(tr[i].r,mid+1,r,qry_l,qry_r);
return ans;
}
TREE query_lml(int i,int l,int r,int qry_l,int qry_r) { //在区间 [qry_l, qry_r] 内部,从 qry_l 开始计算的最大前缀和
if(i==0) return {0,0,0,0,0};
if(l>=qry_l&&r<=qry_r) return tr[i];
int mid=(l+r)/2;
if(qry_l<=mid&&qry_r>mid) { //qry_r>r 不能去等,保证右子树不为空
TREE ls,rs;
ls=query_lml(tr[i].l,l,mid,qry_l,qry_r);
rs=query_lml(tr[i].r,mid+1,r,qry_l,qry_r);
TREE ans;
ans.sum=ls.sum+rs.sum;
ans.lml=max(ls.sum+rs.lml,ls.lml);
return ans;
}
if(qry_l<mid) return query_lml(tr[i].l,l,mid,qry_l,qry_r);
else return query_lml(tr[i].r,mid+1,r,qry_l,qry_r);
}
TREE query_rml(int i,int l,int r,int qry_l,int qry_r) { //在区间 [qry_l, qry_r] 内部,从 qry_l 开始计算的最大前缀和
if(i==0) return {0,0,0,0,0};
if(l>=qry_l&&r<=qry_r) return tr[i];
int mid=(l+r)/2;
if(qry_l<=mid&&qry_r>mid) { //qry_r>r 不能去等,保证右子树不为空
TREE ls,rs;
ls=query_rml(tr[i].l,l,mid,qry_l,qry_r);
rs=query_rml(tr[i].r,mid+1,r,qry_l,qry_r);
TREE ans;
ans.sum=ls.sum+rs.sum;
ans.rml=max(rs.sum+ls.rml,rs.rml);
return ans;
}
if(qry_l<mid) return query_rml(tr[i].l,l,mid,qry_l,qry_r);
else return query_rml(tr[i].r,mid+1,r,qry_l,qry_r);
}
bool cmp(int x,int y) {
return a[x]>a[y];
}
stack<int> s;
int f[20005];//表示位置 i 左边第一个小于 a[i] 的元素的位置
int g[20005];//表示位置 i 右边第一个小于 a[i] 的元素的位置。
main() {
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
int T;
cin>>T;
while(T--) {
int n;
cin>>n;
for(int i=1; i<=n; i++) cin>>a[i],b[i]=i;
sort(b+1,b+1+n,cmp);
rt[0]=build(rt[0],1,n);
for(int i=1; i<=n; i++) rt[i]=update(rt[i],rt[i-1],b[i],1,n);
while(!s.empty()) s.pop();
for (int i = 1; i <= n; i++) f[i] = 0, g[i] = n + 1;
for (int i = 1; i <= n; i++) {
while (s.size() && a[i] < a[s.top()]) g[s.top()] = i, s.pop();
if (s.size()) f[i] = s.top();
s.push(i);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
int x = f[i] + 1, y = g[i] - 1;
int l = 1, r = n;
while (l < r) {
int mid = (l + r) >> 1;
int X = query_rml(rt[mid], 1, n, x, i).rml;
int Y = -query_sum(rt[mid], 1, n, i, i);
int Z = query_lml(rt[mid], 1, n, i, y).lml;
if (X + Y + Z >= 0) r = mid;
else l = mid + 1;
}
ans = max(ans, a[b[l]] - a[i]);
}
cout << ans << '\n';
}
}
代码问题