Loading

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;
}
posted @ 2022-01-14 11:42  Alan_Zhao_2007  阅读(38)  评论(0编辑  收藏  举报