[BZOJ 2653] middle
阿新 • • 發佈:2018-05-24
ddl amp 證明 com http max 構建 問題 digi sum(b,c)+rsum(a,b-1)+lsum(c+1,d)
但明顯不可能對所有的值都單獨構建一棵線段樹,於是我們想到了主席樹
想要有可重用的部分,就要使得建樹的順序具有單調性
於是我們將原數列排序,先將線段樹上所有點都賦為1,然後讓第i個棵樹在第i-1棵樹的基礎上增加一條第i-1個點為-1的鏈即可
Code:
Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2653
Solution:
針對中位數問題的特殊處理:
假設中位數為x,那麽把<x的賦值?1,把>=x的賦值+1,然後看看是否有連續的一段Sum>=0,如有則保證能取到這樣的x
如果在sum大於0則證明答案應該更大,相反答案應該更小。 sum顯然是單調的,所以滿足二分性質,考慮二分答案。 接下來考慮如何維護區間和, 線段樹維護三個值sum,lsum,rsum分別表示區間和,從左端起最大連續子序列和,從右端起最大連續子序列和 滿足條件[a,b][c,d]的最長連續子序列和即為#include <bits/stdc++.h> using namespace std; #define NOW seg[cur] #define LC NOW.ls #define RC NOW.rs inline int read() {char ch;int num,f=0; while(!isdigit(ch=getchar())) f|=(ch==‘-‘); num=ch-‘0‘; while(isdigit(ch=getchar())) num=num*10+ch-‘0‘; return f?-num:num; } template<class T> inline void putnum(T x) { if(x<0)putchar(‘-‘),x=-x; register short a[20]={},sz=0; while(x)a[sz++]=x%10,x/=10; if(sz==0)putchar(‘0‘); for(int i=sz-1;i>=0;i--)putchar(‘0‘+a[i]); putchar(‘\n‘); } const int MAXN=1e6; struct FunTree { int ls,rs; int sum,lsum,rsum; }seg[MAXN]; int n,dat[MAXN],id[MAXN],root[MAXN],cnt=0; bool cmp(int x,int y) { return dat[x]<dat[y]; } void Update(int cur) { NOW.sum=seg[LC].sum+seg[RC].sum; NOW.lsum=max(seg[LC].lsum,seg[LC].sum+seg[RC].lsum); NOW.rsum=max(seg[RC].rsum,seg[RC].sum+seg[LC].rsum); } void Build_Tree(int& cur,int l,int r) { cur=++cnt; if(l==r){NOW.sum=NOW.lsum=NOW.rsum=1;return;} int mid=(l+r)/2; Build_Tree(LC,l,mid);Build_Tree(RC,mid+1,r); Update(cur); } void Insert(int pre,int& cur,int pos,int val,int l,int r) { cur=++cnt; if(l==r){NOW.sum=NOW.lsum=NOW.rsum=-1;return;} int mid=(l+r)>>1;NOW=seg[pre]; if(pos<=mid) Insert(seg[pre].ls,NOW.ls,pos,val,l,mid); else Insert(seg[pre].rs,NOW.rs,pos,val,mid+1,r); Update(cur); } int Query(int a,int b,int cur,int l,int r) { if(a<=l && r<=b) return NOW.sum; int mid=(l+r)>>1,ret=0; if(a<=mid) ret+=Query(a,b,NOW.ls,l,mid); if(b>mid) ret+=Query(a,b,NOW.rs,mid+1,r); return ret; } int Left(int a,int b,int cur,int l,int r) //註意求Left和Right時與求Sum的不同 { if(a==l && b==r) return NOW.lsum; int mid=(l+r)>>1; if(b<=mid) return Left(a,b,NOW.ls,l,mid); if(a>mid) return Left(a,b,NOW.rs,mid+1,r); return max(Left(a,mid,NOW.ls,l,mid),Query(a,mid,NOW.ls,l,mid)+Left(mid+1,b,NOW.rs,mid+1,r)); } int Right(int a,int b,int cur,int l,int r) { if(a==l && b==r) return NOW.rsum; int mid=(l+r)>>1; if(a>mid) return Right(a,b,NOW.rs,mid+1,r); if(b<=mid) return Right(a,b,NOW.ls,l,mid); return max(Right(mid+1,b,NOW.rs,mid+1,r),Query(mid+1,b,NOW.rs,mid+1,r)+Right(a,mid,NOW.ls,l,mid)); } bool check(int x,int a,int b,int c,int d) { int ret1=Query(b,c,root[x],1,n); int ret2=max(Left(c+1,d,root[x],1,n),0); int ret3=max(Right(a,b-1,root[x],1,n),0); return ret1+ret2+ret3>=0; } int main() { n=read(); for(int i=1;i<=n;i++) dat[i]=read(),id[i]=i; sort(id+1,id+n+1,cmp);sort(dat+1,dat+n+1); Build_Tree(root[1],1,n); for(int i=2;i<=n;i++) Insert(root[i-1],root[i],id[i-1],-1,1,n); int T=read(),last=0; while(T--) { int q[5]; for(int i=1;i<=4;i++) q[i]=(read()+last)%n+1; sort(q+1,q+5); int l=1,r=n; while(l<=r) //二分答案 { int mid=(l+r)>>1; if(check(mid,q[1],q[2],q[3],q[4])) l=mid+1; else r=mid-1; } putnum(last=dat[r]); } return 0; }
Review:
1、針對中位數的套路:
把<x的賦值?1,把>=x的賦值+1,然後看看是否有連續的一段Sum>=0
2、主席樹建樹時要使其順序具有一定單調性
3、註意對查找Left、Right時和查找Sum時的區別
[BZOJ 2653] middle