P2839 [国家集训队] middle - 可持久化线段树
题意
定义一个数列 \(\{x_n\}\) 的中位数是 \(x_{\lceil \frac{n}{2}\rceil}\)。给定数列 \(\{a_n\}\),\(q\) 次询问,每次给定 \(a,b,c,d\),求所有满足 \(l\in [a,b],r\in [c,d]\) 的区间 \([l,r]\) 中,\(a_{l\dots r}\) 的中位数最大是多少。强制在线。
\(1\le n,q\le 2.5\times 10^4\)。
题解
我们二分中位数 \(x\)。若将 \(<x\) 的数标记为 \(-1\),\(\ge x\) 的数标记为 \(1\),假如存在一个在询问范围内的区间满足它的标记之和 \(\ge 0\),那么 \(x\) 就是可以取到的中位数。可以发现,当 \(x\gets x+1\) 时,只会有 \(c(x)\) 个位置的标记改变了,其中 \(c(x)\) 为 \(x\) 在 \(a_{1\dots n}\) 中的出现次数。由于 \(\sum_x c(x)=n\),我们将这些改变的位置用线段树修改,并把修改可持久化即可。
时间复杂度 \(\mathcal{O}(n\log n+q\log^2 n)\)。
代码
写得有点丑陋。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
typedef long long ll;
const int N=2e4+5,LogN=18;
struct SegmentTree{
struct Node{
int l,r,s,lmx,rmx;
Node operator+(const Node &rs) const{
if(!l) return rs;
if(!rs.l) return *this;
return {l,rs.r,s+rs.s,max(lmx,s+rs.lmx),max(rs.rmx,rs.s+rmx)};
}
}t[N*LogN];
int ch[N*LogN][2],tot=0;
void Pushup(int p){t[p]=t[ch[p][0]]+t[ch[p][1]];}
int Build(int l,int r){
int p=++tot;t[p].l=l,t[p].r=r;
if(l==r){
t[p].s=t[p].lmx=t[p].rmx=1;
}else{
ch[p][0]=Build(l,(l+r)/2),ch[p][1]=Build((l+r)/2+1,r);
Pushup(p);
}
return p;
}
int Modify(int p,int pos,int k){
int q=++tot;t[q]=t[p],ch[q][0]=ch[p][0],ch[q][1]=ch[p][1];
if(t[q].l==t[q].r){
t[q].s=t[q].lmx=t[q].rmx=k;
}else{
if(pos<=(t[q].l+t[q].r)/2) ch[q][0]=Modify(ch[p][0],pos,k);
else ch[q][1]=Modify(ch[p][1],pos,k);
Pushup(q);
}
return q;
}
Node Query(int p,int l,int r){
if(l>t[p].r||r<t[p].l) return Node();
if(l<=t[p].l&&t[p].r<=r) return t[p];
return Query(ch[p][0],l,r)+Query(ch[p][1],l,r);
}
}seg;
int n,a[N],disca[N],root[N];
vector<int> disc,occ[N];
int main(){
#ifndef zyz
ios::sync_with_stdio(false),cin.tie(nullptr);
#endif
cin>>n;
For(i,1,n) cin>>a[i],disc.push_back(a[i]);
sort(disc.begin(),disc.end());
disc.erase(unique(disc.begin(),disc.end()),disc.end());
For(i,1,n){
disca[i]=lower_bound(disc.begin(),disc.end(),a[i])-disc.begin()+1;
occ[disca[i]].push_back(i);
}
root[1]=seg.Build(1,n);
For(i,2,int(disc.size())){
root[i]=root[i-1];
for(int p:occ[i-1]){
root[i]=seg.Modify(root[i],p,-1);
}
}
int Q,last=0;cin>>Q;
For($,1,Q){
int q[4];
cin>>q[0]>>q[1]>>q[2]>>q[3];
For(i,0,3) q[i]=(q[i]+last)%n+1;
sort(begin(q),end(q));
int l=1,r=disc.size();
while(l<r){
int mid=(l+r+1)>>1;
if(seg.Query(root[mid],q[0],q[1]).rmx+seg.Query(root[mid],q[1]+1,q[2]-1).s+seg.Query(root[mid],q[2],q[3]).lmx>=0)
l=mid;
else
r=mid-1;
}
cout<<(last=disc[l-1])<<'\n';
}
return 0;
}
Written by Alan_Zhao