關於樹的重心的一些性質都沒有理解的很好 在此總結一下。
樹的重心的應用 點分治 點分樹 動態維護樹的重心等等。
關於點分治的復雜度一論 這個其實是套用了 分治的思想 或者是CDQ 的思想 對區間不斷的進行遞歸分治,這個分治可以很快的分出斷點mid 而在樹上我們就無法快速得出這個mid了。
分治區間的復雜度證明 每次都將區間二分 這樣不斷的分支下去 就是logn層 每層我們對每個點進行O(1)的操作的話 那么每層由於加起來點數==n 故總體復雜度是nlogn的。
但是我們只進行分治不進行運算的話 猶如線段樹的建樹過程 每個點都沒有看 只看區間 那么顯然的是 這個復雜度是一個常數時間我們發現這個二叉樹的節點個數為2n 所以復雜度為2n
這是通過整體來看復雜度。接下來觀察點分治的復雜度來源。
首先我們先選取重心 此時保證每個子樹都<=當前分治的所有節點的一半然后對子樹再進行這個分治的過程 經過觀察 發現只遞歸了logn層 其中每層點的個數加起來約等於總點數 故當把每個點都掃一遍的時候復雜度 為nlogn
如果加入一些其他的操作的話就是nlogn^2的了。那關於在分治之前的 求樹的重心的操作呢 具體的我們發現每一層中的點數為n 然后對當前這層總體求 重心總體復雜度為O(n) 故復雜度也為nlogn。
考慮點分樹 點分樹其實就是把點分治中的那些可以當做重心的點 以某種形式連接起來。這樣我們做動態的修改信息的時候從某個節點暴力的向上跳的復雜度 嚴格logn 而構出這個點分樹的復雜度也為nlogn。(當然我還不會寫)
考慮如何動態維護樹的重心 即刪除當前樹的某個節點 在某個節點出再加一個兒子或者再加一棵樹,這樣的話我們需要LCT動態的維護樹的重心了,維護這個東西需要先知道幾個知識點:
1 重新說明樹的重心 從子樹大小來看最大的子樹<=整棵樹的的一半 從距離上來看 樹上所有點到重心的距離之和<=所有點到另外一點的樹的重心之和。這個很容易思考的腦補一下就好了。
2 此外值得一提的是樹的重心至多有兩個 這個也很容易證明:我們在比較樹的重心的時候一定是某一條邊上的兩個節點之間的比較如果這兩個點都是重心的話此時考慮添加第三個重心添到這兩個點的子樹之中一定不可能。連接某個節點此時樹的重心變為這個節點了 以為以剛加這個點為重心 子樹大小是超過整體的一半的,對於剛剛另一個點也是如此。
綜上 樹的重心最多有兩個。同時擁有奇數個節點的樹只有一個重心。
3 把兩棵樹通過某一個點相連得到一顆新的樹,新的樹的重心必然在連接原來兩顆樹的重心的路徑上。
這個很顯然 的就可以證明出來了 如果不會的話認識我的人可以找我 當面證明 口述比較直觀。描述不太好。
4 一棵樹添加或者刪除一個節點,樹的重心最多只移動一條邊的位置。這個超級的顯然對吧。
此題讓我動態維護樹的重心 兩顆樹連在一起如何快速尋找到重心 顯然我們可以不斷的在重心之間的路徑上苟,然后尋找新的重心 由於是splay 所以比較平衡我們每次暴力找復雜度logn 樹和樹之間的合並均攤logn
故整體復雜度mlogn。

//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<ctime> #include<cstring> #include<string> #include<ctime> #include<cctype> #include<cstdio> #include<utility> #include<queue> #include<stack> #include<deque> #include<map> #include<set> #include<bitset> #include<vector> #include<algorithm> #include<cstdlib> using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n,ans,m,top; char ch; int c[MAXN][2],s[MAXN],f[MAXN],sz[MAXN],si[MAXN],r[MAXN],fa[MAXN]; inline int get(int x){return c[f[x]][1]==x||c[f[x]][0]==x;} inline int getfather(int x){return x==fa[x]?x:fa[x]=getfather(fa[x]);} inline void swap(int &x,int &y){int tmp=x;x=y;y=tmp;} inline void reversal(int x) { swap(c[x][0],c[x][1]); r[x]^=1; } inline void pushup(int x) { sz[x]=sz[c[x][0]]+sz[c[x][1]]+si[x]+1; } inline void pushdown(int x) { if(r[x]) { reversal(c[x][0]); reversal(c[x][1]); r[x]=0; } } inline void rotate(int x) { int old=f[x],oldf=f[old],k=c[old][1]==x; c[old][k]=c[x][k^1];c[x][k^1]=old; if(get(old))c[oldf][c[oldf][1]==old]=x; if(c[old][k])f[c[old][k]]=old; f[x]=oldf;f[old]=x;pushup(old); } inline void splay(int x) { top=0;int y=x; s[++top]=y; while(get(y))s[++top]=y=f[y]; while(top)pushdown(s[top--]); while(get(x)) { int old=f[x],oldf=f[old]; if(get(old))rotate(((c[old][0]==x)^(c[oldf][0]==old))?x:old); rotate(x); } pushup(x); } inline void access(int x) { for(int y=0;x;x=f[y=x]) splay(x),si[x]+=sz[c[x][1]]-sz[y],c[x][1]=y,pushup(x); } inline void make_root(int x) { access(x);splay(x);reversal(x); } inline void extract(int x,int y) { make_root(x); access(y); splay(y); } inline void Link(int x,int y) { extract(x,y); f[x]=y;si[y]+=sz[x]; pushup(y); } inline int merge(int x) { int mid=sz[x]>>1,odd=sz[x]&1; int ls=0,rs=0,maxx=n+1; while(x) { pushdown(x); int l=ls+sz[c[x][0]],r=rs+sz[c[x][1]]; if(l<=mid&&r<=mid) { if(x<maxx)maxx=x; if(odd)break; } if(l>r)rs+=sz[c[x][1]]+si[x]+1,x=c[x][0]; else ls+=sz[c[x][0]]+si[x]+1,x=c[x][1]; } splay(maxx); return maxx; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;++i)fa[i]=i,sz[i]=1,ans^=i; for(int i=1;i<=m;++i) { ch=getc(); int x,y; while(ch!='A'&&ch!='Q'&&ch!='X')ch=getc(); if(ch=='X') { ch=getc();ch=getc(); printf("%d\n",ans); } if(ch=='A') { x=read();y=read(); Link(x,y); int fx=getfather(x); int fy=getfather(y); extract(fx,fy);//提取 提煉 int root=merge(fy); ans=ans^fx^fy^root; fa[fx]=fa[fy]=fa[root]=root; } if(ch=='Q')x=read(),printf("%d\n",getfather(x)); } return 0; }