【洛谷1501】[國家集訓隊] Tree II(LCT維護懶惰標記)
阿新 • • 發佈:2018-12-23
大致題意: 有一棵初始邊權全為\(1\)的樹,四種操作:將兩點間路徑邊權都加上一個數,刪一條邊、加一條新邊,將兩點間路徑邊權都加上一個數,詢問兩點間路徑權值和。
序列版
這道題有一個序列版:【洛谷3373】【模板】線段樹 2。
看題目就知道是一道線段樹板子題。
這種題目移到樹上路徑中,且要刪邊加邊,是\(LCT\)無疑了。
\(LCT\)維護懶惰標記
可以說,這道題就是上面那題的翻版。
同樣維護兩個標記:乘法標記和加法標記,加上原有的翻轉標記,共三個標記。
具體細節其實可以詳見上面提到的那道線段樹板子題,這裡就不多說了。
主要是要注意標記下傳與更新的優先順序
程式碼
#include<bits/stdc++.h> #define N 100000 #define MOD 51061 #define swap(x,y) (x^=y^=x^=y) #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD)) using namespace std; int n,ee,lnk[N+5]; class Class_FIO { private: #define Fsize 100000 #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++) #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch)) int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize]; public: Class_FIO() {A=B=Fin;} inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));} inline void readc(char &x) {while(isspace(x=tc()));} inline void writeln(int x) {while(Stack[++Top]=x%10+48,x/=10);while(Top) pc(Stack[Top--]);pc('\n');} inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;} }F; class Class_LCT//LCT板子 { private: #define LCT_SIZE N #define PushUp(x) (node[x].Size=node[node[x].Son[0]].Size+node[node[x].Son[1]].Size+1,node[x].Sum=(node[x].Val+node[node[x].Son[0]].Sum+node[node[x].Son[1]].Sum)%MOD) #define MulVal(x,v) (node[x].Sum=1LL*node[x].Sum*v%MOD,node[x].Val=1LL*node[x].Val*v%MOD,node[x].flag1=1LL*node[x].flag1*v%MOD,node[x].flag2=1LL*node[x].flag2*v%MOD) #define AddVal(x,v) (Inc(node[x].Sum,1LL*node[x].Size*v%MOD),Inc(node[x].Val,v),Inc(node[x].flag2,v)) #define Rever(x) (swap(node[x].Son[0],node[x].Son[1]),node[x].Rev^=1) #define PushDown(x)\ (\ node[x].flag1^1&&(MulVal(node[x].Son[0],node[x].flag1),MulVal(node[x].Son[1],node[x].flag1),node[x].flag1=1),\ node[x].flag2&&(AddVal(node[x].Son[0],node[x].flag2),AddVal(node[x].Son[1],node[x].flag2),node[x].flag2=0),\ node[x].Rev&&(Rever(node[x].Son[0]),Rever(node[x].Son[1]),node[x].Rev=0)\ ) #define Which(x) (node[node[x].Father].Son[1]==x) #define Connect(x,y,d) (node[node[x].Father=y].Son[d]=x) #define IsRoot(x) (node[node[x].Father].Son[0]^x&&node[node[x].Father].Son[1]^x) #define MakeRoot(x) (Access(x),Splay(x),Rever(x)) #define Split(x,y) (MakeRoot(x),Access(y),Splay(y)) int Stack[LCT_SIZE+5]; struct Tree { int Val,Sum,Size,flag1,flag2,Rev,Father,Son[2]; }node[LCT_SIZE+5]; inline void Rotate(int x) { register int fa=node[x].Father,pa=node[fa].Father,d=Which(x); !IsRoot(fa)&&(node[pa].Son[Which(fa)]=x),node[x].Father=pa,Connect(node[x].Son[d^1],fa,d),Connect(fa,x,d^1),PushUp(fa),PushUp(x); } inline void Splay(int x) { register int fa=x,Top=0; while(Stack[++Top]=fa,!IsRoot(fa)) fa=node[fa].Father; while(Top) PushDown(Stack[Top]),--Top; while(!IsRoot(x)) fa=node[x].Father,!IsRoot(fa)&&(Rotate(Which(x)^Which(fa)?x:fa),0),Rotate(x); } inline void Access(int x) {for(register int son=0;x;x=node[son=x].Father) Splay(x),node[x].Son[1]=son,PushUp(x);} inline int FindRoot(int x) {Access(x),Splay(x);while(node[x].Son[0]) PushDown(x),x=node[x].Son[0];return Splay(x),x;} public: inline void Init(int len) {for(register int i=1;i<=len;++i) node[i].Val=node[i].flag1=1;} inline void Link(int x,int y) {MakeRoot(x),FindRoot(y)^x&&(node[x].Father=y);} inline void Cut(int x,int y) {MakeRoot(x),!(FindRoot(y)^x)&&!(node[y].Father^x)&&!node[y].Son[0]&&(node[y].Father=node[x].Son[1]=0,PushUp(x));} inline void Mul(int x,int y,int v) {Split(x,y),MulVal(y,v);} inline void Add(int x,int y,int v) {Split(x,y),AddVal(y,v);} inline int Query(int x,int y) {return Split(x,y),node[y].Sum;} }LCT; int main() { register int query_tot,i,x,y,z;register char op; for(F.read(n),F.read(query_tot),LCT.Init(n),i=1;i<n;++i) F.read(x),F.read(y),LCT.Link(x,y); while(query_tot--) { F.readc(op),F.read(x),F.read(y); switch(op) { case '*':F.read(z),LCT.Mul(x,y,z);break; case '+':F.read(z),LCT.Add(x,y,z);break; case '-':LCT.Cut(x,y),F.read(x),F.read(y),LCT.Link(x,y);break; case '/':F.writeln(LCT.Query(x,y));break; } } return F.clear(),0; }