●洛谷P3348 [ZJOI2016]大森林
阿新 • • 發佈:2018-03-10
ems href define r+ pan bits found www AD
也就不會有情況[2]了。
所以,我們可以記錄下來每種節點x的在哪段區間裏L[x],R[x]。
然後對於一個1操作(1,l,r,x),我們就可以得到其真正會影響的那段連續區間[max(l,L[x]),min(r,R[x])]
再強調一下,對於計算出來的區間[max(l,L[x]),min(r,R[x])],該操作是一定會影響到的,同時也只會影響到該區間。
然後再看看由於詢問保證一定給出的點存在,
所以既然我們都已經得到了1操作會真正影響的區間,那麽對於所有的0操作(生長操作),
我們就可以忽略掉其給出的區間限制,而讓整個[1,N]的區間都執行這個操作。
這樣並不會影響答案。
(因為詢問不會問到這個點,1操作也因為已經算出了其確切會影響的區間)。
由於樹很多,不能同時維護這麽多顆樹,我們考慮離線,並用LCT維護
把所有操作和詢問按其影響的區間的端點掛在對應的位置上,
依次維護每一顆樹,並回答關於該樹的問題。
同樣由於詢問保證一定給出的點存在,所以我們把詢問放在最後來做。(即把當前樹的形態維護好後,再回答所有詢問)
然後是如何維護樹的形態,
1.對於一個每一個1操作(1,l,r,x),我們都新建一個虛節點,權值為0,
初始時該點先連在上一個虛節點上,之後所有的點都連在這個新的虛節點上面。
然後當到了第[max(l,L[x])顆樹時,就讓它連著它下面那一包東西,
先和它的父親斷開,再整體連到x對應的節點上去,實現了把之後的節點長在x上操作。
然後當到了第min(r,R[x])]+1顆樹時,就讓該虛點又連著它下面的那一包東西,回到之前的位置上去,實現了該操作沒有影響的區間,生長節點不動這個操作。
2.對於一個0操作,因為我們讓它對全區間生效,
所以只要遇到0操作,就把該操作對應的權值為1的節點link到上一個虛節點,以便之後隨著虛節點被一起打包著走。
3.對於2操作,現在需要詢問了,由於該樹的形態已經維護好,所以直接找到兩個點lca並回答即可。
(思路太妙啊,我都不知道下次遇到類似的題目能不能想出這種解法。2333)
題鏈:
https://www.luogu.org/problemnew/show/P3348
題解:
LCT,神題
首先有這麽一個結論:
每次的1操作(改變生長點操作),一定只會會對連續的一段區間產生影響。
(即不存在對兩段不相連的區間都進行了該操作的情況,令這種情況為[2])
簡單來說就是因為該操作需要對應的那些樹存在那個節點。
如果發生了情況[2],即表明區間[l1,r1],[l2,r2]有節點x,且[l1+1,r2-1]無節點x,且r2-1>=l1+1
但是我們的生長操作0操作每次進行的區間都是連續的,且每次產生的節點的編號都不同,
所以不會存在"區間[l1,r1],[l2,r2]有節點x,且[l1+1,r2-1]無節點x,且r2-1>=l1+1"這種情況
也就不會有情況[2]了。
所以,我們可以記錄下來每種節點x的在哪段區間裏L[x],R[x]。
然後對於一個1操作(1,l,r,x),我們就可以得到其真正會影響的那段連續區間[max(l,L[x]),min(r,R[x])]
再強調一下,對於計算出來的區間[max(l,L[x]),min(r,R[x])],該操作是一定會影響到的,同時也只會影響到該區間。
然後再看看由於詢問保證一定給出的點存在,
所以既然我們都已經得到了1操作會真正影響的區間,那麽對於所有的0操作(生長操作),
我們就可以忽略掉其給出的區間限制,而讓整個[1,N]的區間都執行這個操作。
這樣並不會影響答案。
(因為詢問不會問到這個點,1操作也因為已經算出了其確切會影響的區間)。
由於樹很多,不能同時維護這麽多顆樹,我們考慮離線,並用LCT維護
把所有操作和詢問按其影響的區間的端點掛在對應的位置上,
依次維護每一顆樹,並回答關於該樹的問題。
同樣由於詢問保證一定給出的點存在,所以我們把詢問放在最後來做。(即把當前樹的形態維護好後,再回答所有詢問)
然後是如何維護樹的形態,
1.對於一個每一個1操作(1,l,r,x),我們都新建一個虛節點,權值為0,
初始時該點先連在上一個虛節點上,之後所有的點都連在這個新的虛節點上面。
然後當到了第[max(l,L[x])顆樹時,就讓它連著它下面那一包東西,
先和它的父親斷開,再整體連到x對應的節點上去,實現了把之後的節點長在x上操作。
然後當到了第min(r,R[x])]+1顆樹時,就讓該虛點又連著它下面的那一包東西,回到之前的位置上去,實現了該操作沒有影響的區間,生長節點不動這個操作。
2.對於一個0操作,因為我們讓它對全區間生效,
所以只要遇到0操作,就把該操作對應的權值為1的節點link到上一個虛節點,以便之後隨著虛節點被一起打包著走。
3.對於2操作,現在需要詢問了,由於該樹的形態已經維護好,所以直接找到兩個點lca並回答即可。
(思路太妙啊,我都不知道下次遇到類似的題目能不能想出這種解法。2333)
代碼:
#include<bits/stdc++.h> #define MAXN 400005 using namespace std; struct LCT{ int lnt; int ch[MAXN][2],size[MAXN],val[MAXN],fa[MAXN]; LCT(){lnt=1;} bool Who(int x){return ch[fa[x]][0]!=x;} bool Isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} int Newnode(int v){ fa[lnt]=ch[lnt][0]=ch[lnt][1]=0; size[lnt]=val[lnt]=v; return lnt++; } void Pushup(int x){ size[x]=val[x]; if(ch[x][0]) size[x]+=size[ch[x][0]]; if(ch[x][1]) size[x]+=size[ch[x][1]]; } void Rotate(int x){ static int y,z,l1,l2; y=fa[x]; z=fa[y]; l1=Who(x); l2=Who(y); fa[x]=z; if(!Isroot(y)) ch[z][l2]=x; fa[ch[x][l1^1]]=y; fa[y]=x; ch[y][l1]=ch[x][l1^1]; ch[x][l1^1]=y; Pushup(y); } void Splay(int x){ static int y; for(;y=fa[x],!Isroot(x);Rotate(x)) if(!Isroot(y)) Rotate(Who(y)==Who(x)?y:x); Pushup(x); } int Access(int x){ static int y; for(y=0;x;y=x,x=fa[x]) Splay(x),ch[x][1]=y,Pushup(x); return y; } int Findlca(int x,int y){ Access(x); return Access(y); } void Link(int x,int y){ //Splay(y); It‘s not neccessary to do the Splay operation. fa[x]=y; } void Cut(int x){ Access(x); Splay(x); fa[ch[x][0]]=0; ch[x][0]=0; Pushup(x); } }DT; struct Cmd{ int pos,odr,from,to; Cmd(){} Cmd(int _a,int _b,int _c,int _d):pos(_a),odr(_b),from(_c),to(_d){} bool operator < (const Cmd &rtm) const{ return pos<rtm.pos||(pos==rtm.pos&&odr<rtm.odr); } }Q[MAXN]; int id[MAXN],L[MAXN],R[MAXN],ANS[MAXN]; int N,M,unreal,dnt=1,cnt=1; int main(){ ios::sync_with_stdio(0); cin>>N>>M; L[dnt]=1; R[dnt]=N; id[dnt++]=DT.Newnode(1); unreal=DT.Newnode(0); DT.Link(unreal,id[1]); for(int i=1,t,l,r,x,tmp;i<=M;i++){ cin>>t; switch(t){ case 0: cin>>l>>r; L[dnt]=l; R[dnt]=r; id[dnt]=DT.Newnode(1); Q[cnt++]=Cmd(1,i-M,id[dnt],unreal); dnt++; //Can the real node be linked to unreal node at now instead of doing this later? //DT.Link(id[dnt],unreal); //After the try,I found it okay to do this. break; case 1: cin>>l>>r>>x; l=max(l,L[x]); r=min(r,R[x]); if(l<=r){ tmp=DT.Newnode(0); DT.Link(tmp,unreal); Q[cnt++]=Cmd(l,i-M,tmp,id[x]); Q[cnt++]=Cmd(r+1,i-M,tmp,unreal); unreal=tmp; } break; case 2: cin>>x>>l>>r; Q[cnt++]=Cmd(x,i,id[l],id[r]); break; } } sort(Q+1,Q+cnt); memset(ANS,-1,sizeof(ANS)); for(int tree=1,i=1,x,y;i<cnt&&tree<=N;tree++){ while(Q[i].pos==tree){ x=Q[i].from; y=Q[i].to; if(Q[i].odr>0){ int l1,l2,l3,lca; DT.Access(x); DT.Splay(x); l1=DT.size[x]; lca=DT.Findlca(x,y); DT.Access(lca); DT.Splay(lca); l2=DT.size[lca]; DT.Access(y); DT.Splay(y); l3=DT.size[y]; ANS[Q[i].odr]=l1+l3-2*l2; } else DT.Cut(x),DT.Link(x,y); i++; } } for(int i=1;i<=M;i++) if(ANS[i]>=0) cout<<ANS[i]<<endl; return 0; }
●洛谷P3348 [ZJOI2016]大森林