C57 可持久化线段树+二分 P2839 [国家集训队] middle
视频链接:243 可持久化线段树+二分 P2839 [国家集训队] middle_哔哩哔哩_bilibili
#include <iostream> #include <cstring> #include <algorithm> using namespace std; void read(int &x){ x=0; char c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c))x=x*10+c-'0',c=getchar(); } #define N 20010 #define mid ((l+r)>>1) int n,Q,q[4],last; struct A{ int x,id; //数,下标 bool operator<(A &t){return x<t.x;} }a[N]; int root[N],tot; //根节点,开点个数 int ls[N*20],rs[N*20],sum[N*20],lmx[N*20],rmx[N*20]; // sum:区间和,lmx:左最大子段和,rmx:右最大子段和 void pushup(int u){ //上传 sum[u]=sum[ls[u]]+sum[rs[u]]; lmx[u]=max(lmx[ls[u]],lmx[rs[u]]+sum[ls[u]]); rmx[u]=max(rmx[rs[u]],rmx[ls[u]]+sum[rs[u]]); } void build(int &u,int l,int r){ //建树 u=++tot; //动态开点 if(l==r){lmx[u]=rmx[u]=sum[u]=1; return;} build(ls[u],l,mid); build(rs[u],mid+1,r); pushup(u); } void change(int &u,int v,int l,int r,int p,int k){ //点修 u=++tot; //动态开点 ls[u]=ls[v];rs[u]=rs[v]; sum[u]=sum[v];lmx[u]=lmx[v];rmx[u]=rmx[v]; if(l==r){lmx[u]=rmx[u]=sum[u]=k; return;} if(p<=mid)change(ls[u],ls[v],l,mid,p,k); else change(rs[u],rs[v],mid+1,r,p,k); pushup(u); } int qsum(int u,int l,int r,int x,int y){ //区间和 if(x<=l&&r<=y) return sum[u]; if(y<=mid) return qsum(ls[u],l,mid,x,y); if(x>=mid+1) return qsum(rs[u],mid+1,r,x,y); return qsum(ls[u],l,mid,x,mid)+qsum(rs[u],mid+1,r,mid+1,y); } //区间和 int qlmx(int u,int l,int r,int x,int y){ //左最大子段和 if(l>=x&&r<=y) return lmx[u]; if(y<=mid) return qlmx(ls[u],l,mid,x,y); if(x>=mid+1) return qlmx(rs[u],mid+1,r,x,y); return max(qlmx(ls[u],l,mid,x,mid), qsum(ls[u],l,mid,x,mid)+qlmx(rs[u],mid+1,r,mid+1,y)); } //左最大子段和 int qrmx(int u,int l,int r,int x,int y){ //右最大子段和 if(l>=x&&r<=y) return rmx[u]; if(y<=mid) return qrmx(ls[u],l,mid,x,y); if(x>=mid+1) return qrmx(rs[u],mid+1,r,x,y); return max(qrmx(rs[u],mid+1,r,mid+1,y), qsum(rs[u],mid+1,r,mid+1,y)+qrmx(ls[u],l,mid,x,mid)); } //右最大子段和 bool check(int u,int a,int b,int c,int d){ //check int s=0; if(c-1>=b+1)s+=qsum(root[u],1,n,b+1,c-1); s+=qrmx(root[u],1,n,a,b); s+=qlmx(root[u],1,n,c,d); return s>=0; } int main(){ read(n); for(int i=1;i<=n;i++) read(a[i].x),a[i].id=i; sort(a+1,a+n+1); build(root[0],1,n); for(int i=1;i<=n;i++) change(root[i],root[i-1],1,n,a[i].id,-1); read(Q); while(Q--){ read(q[0]),read(q[1]),read(q[2]),read(q[3]); for(int i=0;i<4;i++) q[i]=(q[i]+last)%n+1; sort(q,q+4); int l=0,r=n+1; //二分原序列的下标位置 while(l+1<r) check(mid,q[0],q[1],q[2],q[3])?l=mid:r=mid; last=a[l+1].x; //让区间和变成-1的位置即答案 printf("%d\n",last); } }