題解 P2839 【[國家集訓隊]middle】
題面
給一個序列 \(s\) ,回答 \(Q\) 個這樣的詢問:\(s\) 的左端點在 \([a,b]\) 中,右端點在 \([c,d]\) 中的子區間的最大中位數。
題解
首先要知道中位數怎麼求:
二分出一個 \(mid\) ,判斷中位數 \(m\) 與 \(mid\) 的大小關係。將詢問區間 \([l,r]\) 內所有 \(s_i < mid\) 賦值為 \(-1\) ,\(s_i\geq mid\) 賦值為 \(1\) ,令 \(sum=\sum_{i=l}^rs_i\) 分類討論:
- 若 \(sum \geq 0\) ,則 \(m \geq mid\) 。
- 若 \(sum<0\)
但是由於本題詢問的區間不固定,上面的做法就需要改動。
題目中要求 \(m\) 最大,所以對於一個值 \(mid\) ,我們要讓 \(sum\) 儘量大,而區間 \([b+1,c-1]\) 的 \(sum\) 是一定的,所以等價於區間 \([a,b],[c,d]\) 的 \(sum\) 儘量大,這相當於求區間 \([a,b]\) 和最大的字尾與區間 \([c,d]\) 和最大的字首(這裡的字首字尾長度都不為 \(0\) )。
然後就有一個做法了:對於離散化後中位數可能取到的每一個值開一棵線段樹,維護區間和 \(sum\) 、最大字首和 \(lsum\) 、最大字尾和 \(rsum\)
不難發現值 \(x\) 對應的線段樹與值 \(x+1\) 對應的線段樹差別不大,也就是說我們記錄了很多重複資訊。顯然,由 \(x\) 到 \(x+1\) ,只有原序列中值為 \(x\) 的數對應的值由 \(1\) 變為了 \(-1\) ,這裡就可以用一個類似主席樹的東西繼承值 \(x\) 對應的線段樹上的資訊。
用 \(\text{vector}\) 記錄每個值的出現位置,每次以上一次修改為模板進行修改。
\(\text{Code}:\)
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <cmath> #include <vector> #define maxn 20005 #define Rint register int #define INF 0x3f3f3f3f using namespace std; typedef long long lxl; template <typename T> inline T read() { T x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } int n,m,Q; int A[maxn],B[maxn]; std::vector<int> P[maxn]; int rt[maxn]; struct Segment_Tree { struct node { int sum,lsum,rsum; node(int x=0){sum=lsum=rsum=x;} inline node operator + (const node &T)const { node res; res.sum=sum+T.sum; res.lsum=max(lsum,sum+T.lsum); res.rsum=max(T.rsum,T.sum+rsum); return res; } }tree[maxn<<5]; int tot,ch[maxn<<5][2]; inline int build(int l,int r,int d) { int p=++tot; if(l==r) {tree[p]=node(A[l]>=d?1:-1);return p;} int mid=(l+r)>>1; ch[p][0]=build(l,mid,d); ch[p][1]=build(mid+1,r,d); tree[p]=tree[ch[p][0]]+tree[ch[p][1]]; return p; } inline int update(int l,int r,int pos,int tmp) { int p=++tot; tree[p]=tree[tmp]; ch[p][0]=ch[tmp][0];ch[p][1]=ch[tmp][1]; if(l==r) {tree[p]=node(-1);return p;} int mid=(l+r)>>1; if(pos<=mid) ch[p][0]=update(l,mid,pos,ch[tmp][0]); else ch[p][1]=update(mid+1,r,pos,ch[tmp][1]); tree[p]=tree[ch[p][0]]+tree[ch[p][1]]; return p; } inline node query(int p,int l,int r,int L,int R) { if(L<=l&&r<=R) return tree[p]; int mid=(l+r)>>1; if(R<=mid) return query(ch[p][0],l,mid,L,R); else if(L>mid) return query(ch[p][1],mid+1,r,L,R); else return query(ch[p][0],l,mid,L,R)+query(ch[p][1],mid+1,r,L,R); } }st; int a,b,c,d; inline int check(int mid) { int res=0; if(b+1<=c-1) res+=st.query(rt[mid],1,n,b+1,c-1).sum; res+=st.query(rt[mid],1,n,a,b).rsum; res+=st.query(rt[mid],1,n,c,d).lsum; return res; } int main() { // freopen("P2839.in","r",stdin); n=read<int >(); for(int i=1;i<=n;++i) A[i]=B[i]=read<int >(); sort(B+1,B+n+1); m=unique(B+1,B+n+1)-B-1; for(int i=1;i<=n;++i) { A[i]=lower_bound(B+1,B+m+1,A[i])-B; P[A[i]].push_back(i); } rt[1]=st.build(1,n,1); for(int i=2;i<=m;++i) { rt[i]=rt[i-1]; for(vector<int>::iterator it=P[i-1].begin();it!=P[i-1].end();++it) rt[i]=st.update(1,n,*it,rt[i]); } int x=0,q[4]; Q=read<int >(); while(Q--) { q[0]=(read<int >()+x)%n+1,q[1]=(read<int >()+x)%n+1; q[2]=(read<int >()+x)%n+1,q[3]=(read<int >()+x)%n+1; sort(q,q+4); a=q[0],b=q[1],c=q[2],d=q[3]; int l=1,r=m,res=-1; while(l<=r) { int mid=(l+r)>>1; if(check(mid)>=0) res=mid,l=mid+1; else r=mid-1; } printf("%d\n",x=B[res]); } return 0; }