裸的动态树问题。
回顾一下我们对树的认识。
最初,它是一个连通的无向的无环的图,然后我们发现由一个根出发进行BFS 会出现层次分明的树状图形。
然后根据树的递归和层次性质,我们得到了很多有趣的算法,比如单源最短路等等。
如今,我们面对更复杂的问题,给定一个森林,随时更改树的形态,并询问两个节点之间路径上所有点的权值和。
要求复杂度O(Logn)
我们把树看作由若干条链构成,每个链用一个splay维护。
至此,我们对树的认识已经到了一个比较高的水平了。
关于LCT的详细阐述,参考http://www.cnblogs.com/zinthos/p/3900225.html
代码并不复杂, 一句话 splay的核心是splay(x) LCT的核心是access(x)
此处join(x,y)通过对x的翻转实现,splay(x)后,反转x x就由队尾变成队首此时PNT(x)==null, PNT(x)=y就实现了连接(x,y).
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MaxNode=31000; int Lch[MaxNode]; int Rch[MaxNode]; int Pnt[MaxNode]; int Data[MaxNode]; int Sum[MaxNode]; int Rev[MaxNode]; int List[MaxNode]; int Total; inline bool isRoot(int t){ return (!Pnt[t]||(Lch[Pnt[t]]!=t&&Rch[Pnt[t]]!=t)); } inline void Update(int cur){ Sum[cur]=Sum[Lch[cur]]+Sum[Rch[cur]]+Data[cur]; } void Reverse(int cur){ if (!Rev[cur]) return; swap(Lch[cur],Rch[cur]); Rev[Lch[cur]]^=1; Rev[Rch[cur]]^=1; Rev[cur]=0; } void LeftRotate(int cur){ if (isRoot(cur)) return; int pnt=Pnt[cur],anc=Pnt[pnt]; Lch[pnt]=Rch[cur]; if (Rch[cur]) Pnt[Rch[cur]]=pnt; Rch[cur]=pnt; Pnt[pnt]=cur; Pnt[cur]=anc; if (anc){ if (Lch[anc]==pnt) Lch[anc]=cur; else if (Rch[anc]==pnt) Rch[anc]=cur; } Update(pnt); Update(cur); } void RightRotate(int cur){ if (isRoot(cur)) return; int pnt=Pnt[cur],anc=Pnt[pnt]; Rch[pnt]=Lch[cur]; if (Lch[cur]) Pnt[Lch[cur]]=pnt; Lch[cur]=pnt; Pnt[pnt]=cur; Pnt[cur]=anc; if (anc){ if (Rch[anc]==pnt) Rch[anc]=cur; else if (Lch[anc]==pnt) Lch[anc]=cur; } Update(pnt); Update(cur); } void Splay(int cur){ int pnt,anc; List[++Total]=cur; for (int i=cur;!isRoot(i);i=Pnt[i]) List[++Total]=Pnt[i]; for (;Total;--Total) if (Rev[List[Total]]) Reverse(List[Total]); while (!isRoot(cur)){ pnt=Pnt[cur]; if (isRoot(pnt)){// 父亲是根结点,做一次旋转 if (Lch[pnt]==cur) LeftRotate(cur); else RightRotate(cur); } else{ anc=Pnt[pnt]; if (Lch[anc]==pnt){ if (Lch[pnt]==cur) LeftRotate(pnt),LeftRotate(cur);// 一条线 else RightRotate(cur),LeftRotate(cur);// 相反两次 } else{ if (Rch[pnt]==cur) RightRotate(pnt),RightRotate(cur);// 一条线 else LeftRotate(cur),RightRotate(cur);// 相反两次 } } } } int Expose(int u){ int v=0; for (;u;u=Pnt[u]) Splay(u),Rch[u]=v,v=u,Update(u); for (;Lch[v];v=Lch[v]); return v; } void Modify(int x,int d){ Splay(x); Data[x]=d; Update(x); } int Query(int x,int y){ int rx=Expose(x),ry=Expose(y); if (rx==ry){ for (int u=x,v=0;u;u=Pnt[u]){ Splay(u); if (!Pnt[u]) return Sum[Rch[u]]+Data[u]+Sum[v]; Rch[u]=v; Update(u); v=u; } } return -1; } bool Join(int x,int y){ int rx=Expose(x),ry=Expose(y); if (rx==ry) return false; else{ Splay(x); Rch[x]=0; Rev[x]=1; Pnt[x]=y; Update(x); return true; } } void Cut(int x){ if (Pnt[x]){ Expose(x); Pnt[Lch[x]]=0; Lch[x]=0; Update(x); } } int n,Q; void init(){ Total=0; memset(Rev,0,sizeof(Rev)); memset(Pnt,0,sizeof(Pnt)); memset(Lch,0,sizeof(Lch)); memset(Rch,0,sizeof(Rch)); memset(Sum,0,sizeof(Sum)); } char cmd[22]; int main() { init(); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&Data[i]); scanf("%d",&Q); while (Q--){ int x,y; scanf("%s%d%d",cmd,&x,&y); if (cmd[0]=='p'){ Modify(x,y); } if (cmd[0]=='b'){ if (Join(x,y)) printf("yes\n"); else printf("no\n"); } if (cmd[0]=='e'){ int ans=Query(x,y); if (ans==-1) printf("impossible\n"); else printf("%d\n",ans); } } return 0; }