日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

虚树总结

發布時間:2024/4/11 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 虚树总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

之前的之前

之前的之前,可以做做這題,領略虛樹的思想
洛谷P3320 [SDOI2015]尋寶游戲
我在洛谷上的題解

學習之前的例題

在學虛樹之前,先來看一道題:
洛谷P2495 [SDOI2011]消耗戰
大致題意是這樣的,現在有n個點,n-1條帶權邊的樹,有m個詢問,每個詢問,給出k個點,(其中不包含1號節點),要刪除一些邊使得1號點與給出的點不連通,求刪除邊和的最小值。

乍一看,是不是覺得顯然是樹形dp呢,而且樹形dp還是Θ(n)\Theta(n)Θ(n)的,十分優越
但是,不幸的是,2≤n≤2500002\le n\le2500002n250000的情況下,1≤m≤5000001\le m\le5000001m500000,如果是Θ(nm)\Theta(nm)Θ(nm)顯然會TLE,但是又有一個好消息Σki≤500000\Sigma k_i\le500000Σki?500000

那么考慮一下怎么做,可能有些難想,這個時候就該引入虛樹啦,此題可以用虛樹解決

虛樹的概念

虛樹,就是在有一棵樹的情況下,對于數量較少的點進行詢問時所建的一棵新的樹,虛樹包含詢問的點和詢問的點的lca(最近公共祖先),上面的點被稱為關鍵點。對于兩個關鍵點A,B,它們的連邊上包含著原本樹上兩點之間那條鏈上的關鍵信息(這個信息可以是邊權最大值、邊權最小值、或者是邊權之和,這個取決于實際需要),然后就可以進行樹形dp了,這樣的復雜度是基于詢問的點數的,就可以想象虛樹就是把一棵大樹濃縮成一棵擁有所有你需要的信息的小樹。

建樹的方法

那么怎么建樹呢,比較常見的做法是維護一個棧,里面存儲著一條鏈,每一次加入一個點進行操作。
具體列舉一下步驟吧

預備知識:

用較優的復雜度求lca,以及求兩點之間的距離,一般倍增做,或者樹鏈剖分加前綴和都可以,這都是單次Θ(logn)\Theta(logn)Θ(logn)的,另外呢,要事先求好原樹的dfs序和每個點的深度(這里的深度是指點序的深度,在計算這個深度的時候每條邊都是為1來算),后面要用

詢問操作:

1.輸入每個詢問的點,并且按照dfs序為關鍵字排序
2.將第1個點壓到棧當中,開始構建虛樹
3.枚舉到下一個點u,計算u與棧頂點v的公共祖先lca
4.假設棧中棧頂下方的點為w(若棧中只有1個點就直跳過這一步),若w點的深度大于lca就把v向w連一條邊,并且彈掉v,重復此步,否則就到下一步
5.若lca不是當前的v,那么就把lca和v連邊,把v彈出,讓lca成為棧頂元素(注:這個操作的意思是如果棧頂沒有這個lca那么就壓入),否則不做任何操作
6.最后把u壓入棧中
7.回到3操作枚舉下個點,直到枚舉完了所有點
8.把棧頂v與棧頂下方的點為w連邊,并且把v彈掉,這么做直到棧里只有一個點
9.棧里剩下的點就是虛樹的根了
接下來你就可以開始進行dp等操作了

虛樹的復雜度

虛樹的建樹的復雜度是Θ(klogn)\Theta(klogn)Θ(klogn)的,樹形dp就是Θ(k)\Theta(k)Θ(k)的啦,因為考慮最后虛樹上的關鍵點有詢問的點,和lca,然后每個詢問的點最多產生1個新的lca,所以復雜度就是對的啦

有關虛樹用建樹的幾點技巧

首先棧里面最后剩下的點就是虛樹的根節點,這個很好用
然后對于很多題目建虛樹都要建多次,那么 鄰接表/vector 的同學都要清空 head/vector ,如果 memset/一個個枚舉clear 那肯定會T的,我往往都會在樹形dp時用完邊后進行清空,就非常高效了

開頭的題的題解

對于這一題把1號點作為根,對于每個詢問都建一棵虛樹,然后進行樹形dp,這一題當中可以發現若一個點是另一個點的祖先,那么只需保留深度小的那個點,那么建好的虛樹中只有葉子節點是需要被截斷的,樹形dp可以用f[i][0]表示截斷i點的子節點的最小代價,f[i][1]表示不截斷的最小代價(當然可以少一維,但是為了方便我就這么定義啦)
下面是代碼

#include<cstdio> #include<cctype> namespace fast_IO {const int IN_LEN=10000000,OUT_LEN=10000000;char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}inline void flush(){fwrite(obuf,1,oh-obuf,stdout);} } using namespace fast_IO; #define getchar() getchar_() #define putchar(x) putchar_((x)) typedef long long LL; #define rg register template <typename T> inline T max(const T a,const T b){return a>b?a:b;} template <typename T> inline T min(const T a,const T b){return a<b?a:b;} template <typename T> inline T abs(const T a){return a>0?a:-a;} template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;} template <typename T> inline T gcd(const T a,const T b){if(a%b==0)return b;return gcd(b,a%b);} template <typename T> inline T square(const T x){return x*x;}; template <typename T> inline void read(T&x) {char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x; } template <typename T> void printe(const T x) {if(x>=10)printe(x/10);putchar(x%10+'0'); } template <typename T> inline void print(const T x) {if(x<0)putchar('-'),printe(-x);else printe(x); } inline void judge() {freopen("1.in","r",stdin);freopen("1.out","w",stdout); } #include<cstring> #include<algorithm> const int maxn=250001,maxm=500001; int bit[19]; int n,m,s; int head[maxn],nxt[maxm],tow[maxm],vau[maxm],tmp=1; int who[maxn][19],dis[maxn][19]; int dep[maxn]; inline void addb(const int u,const int v,const int w) {tmp++;nxt[tmp]=head[u];head[u]=tmp;tow[tmp]=v;vau[tmp]=w; } int tid[maxn],tim; inline void dfs(const int u) {tid[u]=++tim;for(rg int i=head[u];i;i=nxt[i]){const int v=tow[i];if(who[u][0]!=v){who[v][0]=u;dis[v][0]=vau[i];dep[v]=dep[u]+1;dfs(v);}} } inline int lca(int a,int b) {if(dep[a]<dep[b])swap(a,b);const int lenth=dep[a]-dep[b];for(rg int i=0;bit[i]<=lenth;i++)if(lenth&bit[i])a=who[a][i];if(a==b)return a;for(rg int i=18;i>=0;i--)if(who[a][i]!=who[b][i])a=who[a][i],b=who[b][i];return who[a][0]; } inline int dist(int a,int b) {rg int ans=0x7fffffff;if(dep[a]<dep[b])swap(a,b);const int lenth=dep[a]-dep[b];for(rg int i=0;bit[i]<=lenth;i++)if(lenth&bit[i])ans=min(ans,dis[a][i]),a=who[a][i];if(a==b)return ans;for(rg int i=18;i>=0;i--)if(who[a][i]!=who[b][i])ans=min(ans,min(dis[a][i],dis[b][i])),a=who[a][i],b=who[b][i];return min(ans,min(dis[a][0],dis[b][0])); } int head_[maxn],nxt_[maxm],tow_[maxm],vau_[maxm],tmp_; inline void addb_(const int u,const int v,const int w) {tmp_++;nxt_[tmp_]=head_[u];head_[u]=tmp_;tow_[tmp_]=v;vau_[tmp_]=w; } int k,h[maxn]; bool cmp(const int x,const int y) {return tid[x]<tid[y]; } int stack[maxn],top; LL dp[maxn][2]; void dfs(const int u,const int fa) {rg bool sign=1;dp[u][0]=dp[u][1]=0;for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v!=fa){dfs(v,u);dp[u][0]+=min(dp[v][0],dp[v][1]+vau_[i]);dp[u][1]+=dp[v][1];sign=0;}}head_[u]=0;if(sign)dp[u][0]=0x7fffffffffffffff;dp[u][1]=min(dp[u][1],dp[u][0]); } int main() { // judge();memset(dis,0x7f,sizeof(dis));bit[0]=1;for(rg int i=1;i<=18;i++)bit[i]=bit[i-1]<<1;read(n),s=1;for(rg int i=1;i<n;i++){int u,v,w;read(u),read(v),read(w);addb(u,v,w),addb(v,u,w);}who[s][0]=s,dep[s]=1;dfs(s);for(rg int j=1;j<=18;j++)for(rg int i=1;i<=n;i++)who[i][j]=who[who[i][j-1]][j-1],dis[i][j]=min(dis[i][j-1],dis[who[i][j-1]][j-1]);read(m);for(rg int i=1;i<=m;i++){read(k);for(rg int j=1;j<=k;j++)read(h[j]);std::sort(h+1,h+k+1,cmp);tmp_=0;rg int sum=0;h[++sum]=h[1];for(rg int j=2;j<=k;j++)if(lca(h[sum],h[j])!=h[sum])h[++sum]=h[j];k=sum;top=0,stack[++top]=1;for(rg int j=1;j<=k;j++){const int LCA=lca(stack[top],h[j]);while(top>1&&dep[stack[top-1]]>dep[LCA]){const int DIS=dist(stack[top-1],stack[top]);addb_(stack[top-1],stack[top],DIS),addb_(stack[top],stack[top-1],DIS);top--;}if(dep[stack[top]]>dep[LCA]){const int DIS=dist(LCA,stack[top]);addb_(LCA,stack[top],DIS),addb_(stack[top],LCA,DIS);top--;}if(!top||dep[stack[top]]<dep[LCA])stack[++top]=LCA;stack[++top]=h[j];}while(top>1){const int DIS=dist(stack[top-1],stack[top]);addb_(stack[top-1],stack[top],DIS),addb_(stack[top],stack[top-1],DIS);top--;}dfs(1,0);print(dp[1][0]);putchar('\n');}return 0; }

再來一題

由于前面那題有一些特殊性質,代碼可能不能作為虛樹的模板,所以呢,現在再引入一道題
洛谷P3233 [HNOI2014]世界樹
簡要題意就是現在有一棵n個點,n-1條邊的樹,給出q個詢問,每次詢問給出mi個重要點,樹上的每一個點都屬于離其最近的重要點,若有多個點滿足條件,那么它屬于這其中編號最小的點,對于每個詢問中需要輸出每個給出的重要點所管轄點的數量。n<=300000,q<=300000,m[1]+m[2]+…+m[q]<=300000
怎么做,很顯然還是通過建虛樹然后進行樹形dp啦,這道題其實有些麻煩,后面的樹形dp賊煩(為了便于理解,我dfs了多遍,導致代碼量大大增加),對于每條虛樹邊都可能屬于一個點或者被瓜分成兩份,對于虛樹上的非關鍵點,首先計算它屬于哪個點,然后算出它的出邊的樹中不屬于自己所在重要點的點的數量(除了朝向自己屬于的重要點的方向,這個可以通過和重要點的距離來判),然后對于每一個重要點所管轄點的數量是n-不屬于自己的點的數量就好了。解釋可能不是很清楚,下面貼出代碼(update:由于寫此份代碼的時候太菜,思路不夠清晰,所以當時為了方便思考導致樹形dp這一部分寫的很長,實際樹形dp的清真實現可以去參考網上的其它代碼):

#include<cstdio> #include<cctype> #include<algorithm> #include<cstring> namespace fast_IO {const int IN_LEN=10000000,OUT_LEN=10000000;char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}inline void flush(){fwrite(obuf,1,oh-obuf,stdout);} } using namespace fast_IO; #define getchar() getchar_() #define putchar(x) putchar_((x)) #define rg register typedef long long LL; template <typename T> inline T max(const T a,const T b){return a>b?a:b;} template <typename T> inline T min(const T a,const T b){return a<b?a:b;} template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;} template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;} template <typename T> inline T abs(const T a){return a>0?a:-a;} template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;} template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);} template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;} template <typename T> inline T square(const T x){return x*x;}; template <typename T> inline void read(T&x) {char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x; } template <typename T> inline void printe(const T x) {if(x>=10)printe(x/10);putchar(x%10+'0'); } template <typename T> inline void print(const T x) {if(x<0)putchar('-'),printe(-x);else printe(x); } const int maxn=300001,maxm=600001; int bit[19]; int n,m,s; int head[maxn],nxt[maxm],tow[maxm],tmp=1; int who[maxn][19],dis[maxn][19]; int dep[maxn]; inline void addb(const int u,const int v) {tmp++;nxt[tmp]=head[u];head[u]=tmp;tow[tmp]=v; } int tid[maxn],tim,size[maxn]; inline void dfs(const int u) {tid[u]=++tim,size[u]=1;for(rg int i=head[u];i;i=nxt[i]){const int v=tow[i];if(who[u][0]!=v){who[v][0]=u;dis[v][0]=1;dep[v]=dep[u]+1;dfs(v);size[u]+=size[v]; }} } inline int lca(int a,int b) {if(dep[a]<dep[b])swap(a,b);const int lenth=dep[a]-dep[b];for(rg int i=0;bit[i]<=lenth;i++)if(lenth&bit[i])a=who[a][i];if(a==b)return a;for(rg int i=18;i>=0;i--)if(who[a][i]!=who[b][i])a=who[a][i],b=who[b][i];return who[a][0]; } inline int dist(int a,int b) {rg int ans=0;if(dep[a]<dep[b])swap(a,b);const int lenth=dep[a]-dep[b];for(rg int i=0;bit[i]<=lenth;i++)if(lenth&bit[i])ans+=dis[a][i],a=who[a][i];if(a==b)return ans;for(rg int i=18;i>=0;i--)if(who[a][i]!=who[b][i])ans+=dis[a][i]+dis[b][i],a=who[a][i],b=who[b][i];return ans+dis[a][0]+dis[b][0]; } int head_[maxn],nxt_[maxm],tow_[maxm],vau_[maxm],tmp_; inline void addb_(const int u,const int v,const int w) {tmp_++;nxt_[tmp_]=head_[u];head_[u]=tmp_;tow_[tmp_]=v;vau_[tmp_]=w; } int k,h[maxn],ans[maxn],belo[maxn],Dis[maxn]; int qu[maxn]; bool sign[maxn]; bool cmp(const int x,const int y) {return tid[x]<tid[y]; } int stack[maxn],top; inline int check(int u,const int lenth) {for(rg int i=0;bit[i]<=lenth;i++)if(lenth&bit[i])u=who[u][i];return u; } void dfs1_0(const int u,const int fa) {if(sign[u]){for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v!=fa)dfs1_0(v,u);}}else{for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v!=fa){dfs1_0(v,u);if(sign[v]){if(vau_[i]<Dis[u]||vau_[i]==Dis[u]&&v<belo[u])belo[u]=v,Dis[u]=vau_[i];}else{if(vau_[i]+Dis[v]<Dis[u]||vau_[i]+Dis[v]==Dis[u]&&belo[v]<belo[u])belo[u]=belo[v],Dis[u]=vau_[i]+Dis[v];}}}} } void dfs1_1(const int u,const int fa) {if(sign[u]){for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v!=fa)dfs1_1(v,u);}}else{for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v==fa){if(sign[v]){if(vau_[i]<Dis[u]||vau_[i]==Dis[u]&&v<belo[u])belo[u]=v,Dis[u]=vau_[i];}else{if(vau_[i]+Dis[v]<Dis[u]||vau_[i]+Dis[v]==Dis[u]&&belo[v]<belo[u])belo[u]=belo[v],Dis[u]=vau_[i]+Dis[v];}}}for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v!=fa)dfs1_1(v,u);}} } void dfs2(const int u,const int fa) {if(sign[u]){for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v!=fa)dfs2(v,u);}}else{ans[u]=0;for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(belo[v]==belo[u]&&Dis[u]>Dis[v]||v==belo[u])continue;rg int p;if(v!=fa)dfs2(v,u);if(belo[v]==belo[u])ans[u]+=ans[v];else if(sign[v]){if(v!=fa){if((Dis[u]+vau_[i])&1)p=check(v,(Dis[u]+vau_[i])/2);else if(v<belo[u])p=check(v,(Dis[u]+vau_[i])/2);else p=check(v,(Dis[u]+vau_[i])/2-1);ans[u]+=size[p];}else{if((Dis[u]+vau_[i])&1)p=check(u,(Dis[u]+vau_[i])/2-Dis[u]);else if(v>belo[u])p=check(u,(Dis[u]+vau_[i])/2-Dis[u]);else p=check(u,(Dis[u]+vau_[i])/2-Dis[u]-1);ans[u]+=n-size[p];}}else{if(v!=fa){if((Dis[u]+Dis[v]+vau_[i])&1)p=check(v,(Dis[u]+Dis[v]+vau_[i])/2-Dis[v]);else if(belo[v]<belo[u])p=check(v,(Dis[u]+Dis[v]+vau_[i])/2-Dis[v]);else p=check(v,(Dis[u]+Dis[v]+vau_[i])/2-Dis[v]-1);ans[u]+=size[p];}else{if((Dis[u]+Dis[v]+vau_[i])&1)p=check(u,(Dis[u]+Dis[v]+vau_[i])/2-Dis[u]);else if(belo[v]>belo[u])p=check(u,(Dis[u]+Dis[v]+vau_[i])/2-Dis[u]);else p=check(u,(Dis[u]+Dis[v]+vau_[i])/2-Dis[u]-1);ans[u]+=n-size[p];}}}for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(belo[v]==belo[u]&&Dis[u]>Dis[v]||v==belo[u])if(v!=fa)dfs2(v,u);}} } void dfs3(const int u,const int fa) {if(sign[u]){ans[u]=n;for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];rg int p; if(v!=fa)dfs3(v,u);if(belo[v]==u)ans[u]-=ans[v];else if(sign[v]){if(v!=fa){if(vau_[i]&1)p=check(v,vau_[i]/2);else if(v<u)p=check(v,vau_[i]/2);else p=check(v,vau_[i]/2-1);ans[u]-=size[p];}else{if(vau_[i]&1)p=check(u,vau_[i]/2);else if(v>u)p=check(u,vau_[i]/2);else p=check(u,vau_[i]/2-1);ans[u]-=n-size[p];}}else{if(v!=fa){if((Dis[v]+vau_[i])&1)p=check(v,(Dis[v]+vau_[i])/2-Dis[v]);else if(u<belo[v])p=check(v,(Dis[v]+vau_[i])/2-Dis[v]-1);else p=check(v,(Dis[v]+vau_[i])/2-Dis[v]);ans[u]-=size[p];}else{if((Dis[v]+vau_[i])&1)p=check(u,(Dis[v]+vau_[i])/2);else if(u>belo[v])p=check(u,(Dis[v]+vau_[i])/2-1);else p=check(u,(Dis[v]+vau_[i])/2);ans[u]-=n-size[p];}}}}else{for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v!=fa)dfs3(v,u);}} } void dfs4(const int u,const int fa) {if(sign[u]){for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v!=fa)dfs4(v,u);}sign[u]=0;}else{for(rg int i=head_[u];i;i=nxt_[i]){const int v=tow_[i];if(v!=fa)dfs4(v,u);}belo[u]=0;}head_[u]=0,Dis[u]=0x7f7f7f7f; } int main() {memset(Dis,0x7f,sizeof(Dis));bit[0]=1;for(rg int i=1;i<=18;i++)bit[i]=bit[i-1]<<1;read(n),s=1;for(rg int i=1;i<n;i++){int u,v;read(u),read(v);addb(u,v),addb(v,u);}who[s][0]=s,dep[s]=1;dfs(s);for(rg int j=1;j<=18;j++)for(rg int i=1;i<=n;i++)who[i][j]=who[who[i][j-1]][j-1],dis[i][j]=dis[i][j-1]+dis[who[i][j-1]][j-1];read(m);for(rg int i=1;i<=m;i++){read(k);for(rg int j=1;j<=k;j++)read(h[j]),qu[j]=h[j],sign[h[j]]=1,Dis[h[j]]=0;std::sort(h+1,h+k+1,cmp);tmp_=0;top=0,stack[++top]=h[1];for(rg int j=2;j<=k;j++){const int LCA=lca(stack[top],h[j]);while(top>1&&dep[stack[top-1]]>dep[LCA]){const int DIS=dist(stack[top-1],stack[top]);addb_(stack[top-1],stack[top],DIS),addb_(stack[top],stack[top-1],DIS);top--;}if(dep[stack[top]]>dep[LCA]){const int DIS=dist(LCA,stack[top]);addb_(LCA,stack[top],DIS),addb_(stack[top],LCA,DIS);top--;}if(!top||dep[stack[top]]<dep[LCA])stack[++top]=LCA;stack[++top]=h[j];}while(top>1){const int DIS=dist(stack[top-1],stack[top]);addb_(stack[top-1],stack[top],DIS),addb_(stack[top],stack[top-1],DIS);top--;}dfs1_0(stack[1],0);dfs1_1(stack[1],0);dfs2(stack[1],0);dfs3(stack[1],0);dfs4(stack[1],0);for(rg int i=1;i<=k;i++)print(ans[qu[i]]),putchar(' ');putchar('\n');}return flush(),0; }

總結

上面兩道例題差不多覆蓋了差不多虛樹要用到的所以基礎操作了,總結一個虛樹的板子還是挺好的,虛樹寫起來簡單,也挺好用的,復雜度也挺優秀,值得好好研究一番
update by 2018.3:感謝Michael_Li對本篇博客的建議與指正
update by 2019.1:對部分公式進行了美化更新

總結

以上是生活随笔為你收集整理的虚树总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。