1. 程式人生 > >BZOJ.3489.A simple rmq problem(主席樹 Heap)

BZOJ.3489.A simple rmq problem(主席樹 Heap)

不同的 ret lin www. con set 掃描線 ML queue

題目鏈接

\(Description\)

給定一個長為n的序列,多次詢問[l,r]中最大的只出現一次的數。強制在線。

\(Solution\)

我也不知道該怎麽說,反正就是預處理 建主席樹,套堆/set,樹i存l為i,r為[i,n]的答案(這樣就是在某棵樹上單點查maxv了)。
處理好最初的樹,就可以利用主席樹根據前綴建樹的性質,每個點i的建樹只需要處理i位置。
那麽最初的樹就是將所有的數在區間[第一次出現位置,nxt[]-1]中加入這個數。
當左端點i移動時,A[i-1]的貢獻沒了,要刪掉;但是如果A[i-1]在後面出現,則還要在[nxt[A[i-1]],nxt[nxt[A[i-1]]]-1]上加入A[i-1]。(A[i]已經處理了,要麽在建最初樹時要麽在先前建的樹中)

也不是每棵樹每個節點都套堆,堆只需要幫助建主席樹時加加刪刪(還是利用前綴),要用的只是根據堆得到的每個節點的maxv[],所以堆還是4n大就可以。。

和CF那題不同的是這題固定l更方便,那題固定r好些。所以只需要nxt(和最靠前的一個las用來建最初的樹)。

第一次主席樹區間修改。。在信息覆蓋當前區間時直接給節點賦值,return;查詢單點時加上沿路節點的mx[](標記永久化)。
註意新建節點時copy x的mx[]。因為每次刪除是完全刪除上一次的加入,so在未遞歸到完全覆蓋區間前copy mx是對的?真這樣麻煩嗎。。以後是不是該乖乖寫Update()。。


詢問的限制條件可以看成三個維度,即在某三維空間內找權值最大的點,可以用K-D Tree

。出題人說這不應是正解,可以卡,只是數據問題跑的比主席樹快。。可以看這兒。
不過還是K-D Tree更好的應用吧,不想看了,先放著。。
K-D Tree:https://blog.csdn.net/FromATP/article/details/61198101


代碼參考(chao)自:https://blog.csdn.net/u014609452/article/details/51396271。


非強制在線可以直接線段樹+排序掃描線做吧,有個簡化版的題見這兒。


//147248kb  10116ms
#include <queue>
#include <cstdio>
#include <cctype>
#include <algorithm> #define gc() getchar() const int N=1e5+5; int n,Q,A[N],las[N],nxt[N],rcnt,root[N*3],pos[N]; struct Tree//Persistent Segment Tree { #define S N*100//我也不知道這樣搞區間修改的主席樹會有多少節點(2個log?) #define lson son[x][0] #define rson son[x][1] #define ToL l,m,rt<<1 #define ToR m+1,r,rt<<1|1 struct Heap { std::priority_queue<int> h,d; inline void Insert(int x) {h.push(x);} inline void Delete(int x) {d.push(x);} inline void Maintain(){ while(!h.empty()&&!d.empty()&&h.top()==d.top()) h.pop(),d.pop(); } inline int Top(){ Maintain(); return h.empty()?0:h.top(); } }hp[N<<2]; int tot,son[S][2],mx[S]; // inline void Update(int x){//Update沒啥用啊 又不用區間信息 // mx[x]=std::max(mx[lson],mx[rson]); // } void Insert(int &y,int x,int l,int r,int rt,int L,int R,int v)//rt:一般線段樹的節點 存儲堆 { y=++tot; if(L<=l && r<=R){ hp[rt].Insert(v), mx[y]=hp[rt].Top(), son[y][0]=lson, son[y][1]=rson; return ; } int m=l+r>>1; mx[y]=mx[x];//! if(L<=m) if(m<R) Insert(son[y][0],lson,ToL,L,R,v), Insert(son[y][1],rson,ToR,L,R,v); else son[y][1]=rson, Insert(son[y][0],lson,ToL,L,R,v); else son[y][0]=lson, Insert(son[y][1],rson,ToR,L,R,v); } void Delete(int &y,int x,int l,int r,int rt,int L,int R,int v) { y=++tot; if(L<=l && r<=R){ hp[rt].Delete(v), mx[y]=hp[rt].Top(), son[y][0]=lson, son[y][1]=rson; return; } int m=l+r>>1; mx[y]=mx[x];//! if(L<=m) if(m<R) Delete(son[y][0],lson,ToL,L,R,v), Delete(son[y][1],rson,ToR,L,R,v); else son[y][1]=rson, Delete(son[y][0],lson,ToL,L,R,v); else son[y][0]=lson, Delete(son[y][1],rson,ToR,L,R,v); } int Query(int x,int l,int r,int pos) { if(!x) return 0;//這個沒啥用啊... if(l==r) return mx[x]; int m=l+r>>1; if(pos<=m) return std::max(mx[x],Query(lson,l,m,pos)); else return std::max(mx[x],Query(rson,m+1,r,pos)); } }T; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-'0',c=gc()); return now; } int main() { n=read(), Q=read(); for(int i=1; i<=n; ++i) A[i]=read(); for(int i=1; i<=n; ++i) las[i]=n+1; for(int i=n; i; --i) nxt[i]=las[A[i]], las[A[i]]=i; for(int i=n; i; --i)//直接枚舉值域建樹就可以啊 話說倒著插入堆會更快嗎 if(las[i]<=n) T.Insert(root[rcnt],root[rcnt++],1,n,1,las[i],nxt[las[i]]-1,i);//參數順序 pos[1]=root[rcnt];//root[rcnt] not rcnt→_→ for(int i=1; i<n; ++i) { T.Delete(root[rcnt],root[rcnt++],1,n,1,i,nxt[i]-1,A[i]); if(nxt[i]<=n) T.Insert(root[rcnt],root[rcnt++],1,n,1,nxt[i],nxt[nxt[i]]-1,A[i]); pos[i+1]=root[rcnt]; } for(int ans=0,i=1,l,r; i<=Q; ++i) { l=(read()+ans)%n+1, r=(read()+ans)%n+1; if(l>r) std::swap(l,r);//l>r&&(std::swap(l,r),1); printf("%d\n",ans=T.Query(pos[l],1,n,r)); } return 0; }

BZOJ.3489.A simple rmq problem(主席樹 Heap)