題目連接:BZOJ2653php
解題報告:node
好久之前想拿來作聯賽模擬的一道題==ios
然而由於太水了換掉了233333數據結構
顯然若是咱們枚舉區間端點的話,複雜度過高。ui
考慮換個思路:咱們對於每一個詢問二分一個答案x,表示序列中第x小的數,表示看這個數是否可行,能夠把這個區間內第x小的數,到第n小的數(最大的數)標爲1,最小的數到第x-1小的數的標爲-1,若是區間和大於等於0那麼可行,不然不可行。spa
咱們從簡單處入手,考慮若是隻作一次怎麼作。blog
由於咱們由原序列和x的關係獲得了一個由-1和1所構成的序列(如下稱爲P序列),咱們想知道此時x是否可行,也就是說咱們須要在題目要求的左端點區間[l1,r1]找到一個左端點,右端點區間[l2,r2]找到一個右端點,而後使得這一段的和大於等於0,就說明x可行。排序
也就是說對於序列a,咱們若是有∑ai(l<=i<=r,且l屬於[l1,r1],r屬於[l2,r2])大於等於0便可說明可行。get
那麼咱們是否是必定要枚舉左右端點呢?string
其實不須要,由於咱們只須要算出∑ai(l<=i<=r,且l屬於[l1,r1],r屬於[l2,r2])最大的時候是否大於等於0就能夠完成斷定了。
這也就等價於,對於[l1,r2]求最大子段和,且必須包含[r1+1,l2-1]這一段。
求法很簡單,就是求[l1,r1]的最大後綴子段和,和[l2,r2]的最大前綴子段和,加上[r1+1,l2-1]的總和便可。
以上用一個線段樹就能夠搞定,線段樹維護區間的和、區間最大後綴子段和、區間最大前綴子段和。
這是一次的操做,那麼屢次的呢?
其實關鍵就在於對於每次二分不一樣的x的時候,是否是都要根據x的值重構一遍P序列和新的線段樹?
每次暴力修改而且創建顯然不可行,就必須另闢蹊徑。
若是咱們已經給定了一個比x-1的P序列的狀況,那麼若想將其表示爲x的狀況,則只須要把x-1在x的P序列中標爲-1就能夠了。
也就是說,咱們須要充分運用之前的修改,不須要每次從新修改。
因此咱們但願一個數據結構來幫助咱們複製一份而且每次修改一段,顯然主席樹能夠勝任。
第i棵主席樹維護第i小的數的把全部數表示成一、-1的狀況,而且維護前綴最大值、後綴最大值、區間和。
至關因而有n棵線段樹,每棵都維護的是一個P序列。
這樣一來咱們能夠排序以後,從1到n構主席樹,構i的主席樹的時候每次只把i-1在i的主席樹中對應的位置改爲-1,因此每次在主席樹上須要修改一條鏈。
這樣就完美解決了這個問題了,細節有一點多,寫的時候注意一下。
時間複雜度:O(n log n+m log n log n)
直接貼一年前的代碼好了...
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <ctime> #include <vector> #include <queue> #include <map> #include <set> using namespace std; typedef long long LL; const int MAXN = 20011; const int inf = (1<<30); int n,ans,cnt,daan,jizhu; int wen[5],zheng,ql,qr; int root[MAXN];//每棵主席樹的根的編號 struct node{ int lmax,rmax,sum; int lson,rson; }a[MAXN*20],tmp,yuan; struct Num{ int id,val; }b[MAXN]; inline int getint() { int w=0,q=0; char c=getchar(); while((c<'0' || c>'9') && c!='-') c=getchar(); if (c=='-') q=1, c=getchar(); while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w; } inline void update(int root){ a[root].sum=a[a[root].lson].sum+a[a[root].rson].sum; a[root].lmax=max(a[a[root].lson].lmax, a[a[root].lson].sum+a[a[root].rson].lmax ); a[root].rmax=max(a[a[root].rson].rmax, a[a[root].rson].sum+a[a[root].lson].rmax ); } inline void build(int l,int r,int &now){ cnt++; now=cnt; if(l==r) { a[now].lmax=a[now].rmax=a[now].sum=1; return ; } int mid=(l+r)/2; build(l,mid,a[now].lson); build(mid+1,r,a[now].rson); update(now); } inline void insert(int last,int pos,int l,int r,int &now){ cnt++; now=cnt; a[now]=a[last]; if(l==r) { a[now].lmax=a[now].rmax=a[now].sum=-1; return ; } int mid=(l+r)/2; if(pos<=mid) insert(a[last].lson,pos,l,mid,a[now].lson); else insert(a[last].rson,pos,mid+1,r,a[now].rson); update(now); } inline bool cmp(Num q,Num qq){ return q.val<qq.val; } inline node hebing(node q,node qq){//合併 node xin=yuan; xin.rmax=max(qq.rmax,qq.sum+q.rmax); xin.lmax=max(q.lmax,q.sum+qq.lmax); xin.sum=q.sum+qq.sum;//!!!!!!! return xin; } inline void query_lmax(int root,int l,int r){//查詢前綴最大值 if(ql<=l && r<=qr) { if(!zheng) tmp=a[root],zheng=1; else tmp=hebing(tmp,a[root]); return ; } int mid=(l+r)/2; if(ql<=mid) query_lmax(a[root].lson,l,mid); if(qr>mid) query_lmax(a[root].rson,mid+1,r); } inline void query_rmax(int root,int l,int r){//查詢後綴最大值 if(ql<=l && r<=qr) { if(!zheng) tmp=a[root],zheng=1; else tmp=hebing(tmp,a[root]); return ; } int mid=(l+r)/2; if(ql<=mid) query_rmax(a[root].lson,l,mid); if(qr>mid) query_rmax(a[root].rson,mid+1,r); } inline void query_sum(int root,int l,int r){ if(ql>qr) return ; if(ql<=l && r<=qr) { daan+=a[root].sum; return ; } int mid=(l+r)/2; if(ql<=mid) query_sum(a[root].lson,l,mid); if(qr>mid) query_sum(a[root].rson,mid+1,r); } inline bool check(int x){ ql=wen[1]; qr=wen[2]; zheng=0; tmp=yuan; query_rmax(root[x],1,n); int nowl=tmp.rmax; ql=wen[3]; qr=wen[4]; zheng=0; tmp=yuan; query_lmax(root[x],1,n); int nowr=tmp.lmax; ql=wen[2]+1; qr=wen[3]-1; daan=0; query_sum(root[x],1,n);//中間一段必定會選入區間 int li=nowl+nowr+daan; if(li>=0) return true; return false; } inline void work(){ n=getint(); for(int i=1;i<=n;i++) b[i].val=getint(),b[i].id=i; sort(b+1,b+n+1,cmp); build(1,n,root[1]);//初始均爲1 for(int i=2;i<=n;i++) insert(root[i-1],b[i-1].id,1,n,root[i]);//把前一個設爲-1 int q=getint(); int l,r;int mid; while(q--) { //for(int i=1;i<=4;i++) wen[i]=getint()+1; for(int i=1;i<=4;i++) wen[i]+=jizhu,wen[i]%=n,wen[i]++; sort(wen+1,wen+4+1); l=1; r=n; while(l<=r) { mid=(l+r)/2; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } jizhu=b[ans].val; printf("%d\n",jizhu); } } int main() { work(); return 0; }