動態維護樹的直徑

打2019ICPC上海網絡賽的時候,碰到了一道題(A),可以轉化成動態維護樹的直徑的模型。但是由於不會寫,就想要學習該算法,發現方法還多,一點一點學把。

首先是例題:https://nanti.jisuanke.com/t/41398

方法一:最容易理解和暴力的方法(樹剖LCA+線段樹)

樹剖維護樹的LCA,線段樹合併時維護樹的直徑。

思路:

1.首先要會樹剖,用樹剖處理該樹,得到一個線段樹維護的DFS序(其實就是樹剖)。

2.建立一顆線段樹,區間[l,r]表示包含點l,r中間的所有點對的最長直徑,同時維護直徑的兩端點。

關鍵就在於維護直徑和直徑的兩端點。就在於線段樹合併。

合併的時候,採取最暴力4次比較,即:左子樹會有一個直徑,右子樹會有一個直徑,分別是四個點,分別比較四個點之間的LCA,同時獲得最長的直徑。O(4*long n)。

查詢的時候,整棵樹的直徑,就是維護直徑的線段樹的跟所表示的(x,y)和l。

注意,這個方法複雜度較大,跑1e5的數據(1e5個點,1e5次查詢)需要的時間大概是4000+ms左右,如果常數優化可能會快一點,但是估計很有限,所以如果時間在1000ms的話,估計只能處理1e4的數據量了。

(這個是沒有常數優化的)

下面就是代碼了,寫的可能比較亂(一開始用的類,結果反而麻煩了QAQ)

但是個人感覺還是比較清楚的,兩個類實現的兩個功能:第一個是樹剖動態維護LCA。第二個是線段樹動態維護樹的直徑。

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();

#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
 
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
      
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=998244353;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

/*
首先維護樹的直徑。
DFS序+線段樹可求出LCA距離。
對於每次更新,點下方的所有點都要更新。
查找時,找到樹的直徑,比較兩端點的距離 
*/
struct Edge1{
	int v,val,nxt;
	Edge1(int _v=0,int _val=0,int _nxt=0){
		v=_v;val=_val;nxt=_nxt;
	}
};
Edge1 Edge[MAXN<<2];
int Head[MAXN],Ecnt;
struct TreeCut{//樹剖 
	int Deep[MAXN],Fa[MAXN],Size[MAXN],Son[MAXN];
	int Idx[MAXN],RIdx[MAXN],Icnt;
	int Top[MAXN];
	int A[MAXN],B[MAXN];
	
	int DFS1(int u,int fa,int dep){
		Deep[u]=dep;Fa[u]=fa;Size[u]=1;
		int maxson=-1;
		for(int i=Head[u];i+1;i=Edge[i].nxt){
			int v=Edge[i].v;
			if(v==fa) continue ;
			Size[u]+=DFS1(v,u,dep+1);
			if(Size[v]>maxson){
				maxson=Size[v];
				Son[u]=v;
			}
		}return Size[u];
	}
	void DFS2(int u,int topfa){
		Idx[u]=++Icnt;RIdx[Icnt]=u;B[Idx[u]]=A[u];Top[u]=topfa;
		if(Son[u]==-1) return ;
		DFS2(Son[u],topfa);
		for(int i=Head[u];i+1;i=Edge[i].nxt){
			int v=Edge[i].v;
			if(Idx[v]==0) DFS2(v,v);
		}
	}
	
	struct Node{
		int l,r,len;
		ll sum;
	};
	Node Tree[MAXN<<2];
	
	void PushUp(int rt){
		Tree[rt].sum=Tree[rt<<1].sum+Tree[rt<<1|1].sum;
	}
	void Build(int l,int r,int rt){
		Tree[rt].l=l;Tree[rt].r=r;Tree[rt].len=r-l+1;
		if(l==r){
			Tree[rt].sum=B[l];
			return ;
		}int mid=(l+r)>>1;
		Build(l,mid,rt<<1);Build(mid+1,r,rt<<1|1);
		PushUp(rt);
	}
	void Update(int pos,int val,int rt){
		if(Tree[rt].l==Tree[rt].r){
			Tree[rt].sum=val;
			return ;
		}
		if(Tree[rt<<1].r>=pos) Update(pos,val,rt<<1);
		else Update(pos,val,rt<<1|1);
		PushUp(rt);
	}
	ll Query(int ql,int qr,int rt){
		if(ql<=Tree[rt].l&&Tree[rt].r<=qr) return Tree[rt].sum;
		if(ql>=Tree[rt<<1|1].l) return Query(ql,qr,rt<<1|1);
		else if(qr<=Tree[rt<<1].r) return Query(ql,qr,rt<<1);
		else{
			return Query(ql,qr,rt<<1)+Query(ql,qr,rt<<1|1);
		}
	}
	ll QueryLCA(int l,int r){//得到[l~r]的距離 
		ll ans=0;
		while(Top[l]!=Top[r]){
			if(Deep[Top[l]]<Deep[Top[r]]) swap(l,r);
			ans+=Query(Idx[Top[l]],Idx[l],1);
			l=Fa[Top[l]];
		}
		if(Deep[l]>Deep[r]) swap(l,r);
		ans+=Query(Idx[l],Idx[r],1);ans-=Query(Idx[l],Idx[l],1);
		return ans;
	}
	void Show(int rt){
		printf("l=%d r=%d sum=%lld\n",Tree[rt].l,Tree[rt].r,Tree[rt].sum);
		if(Tree[rt].l==Tree[rt].r) return ;
		Show(rt<<1);Show(rt<<1|1);
	}
};
TreeCut LCA;
struct TreeDia{//維護樹的直徑 
	struct Node{
		int l,r,len;
		int x,y;
		ll dis;
		Node(int _x=0,int _y=0,ll _dis=0){
			x=_x;y=_y;dis=_dis;
		}
	};
	Node Tree[MAXN<<2];
	
	void PushUp(int rt){
		Tree[rt]=Tree[rt<<1].dis>Tree[rt<<1|1].dis?Tree[rt<<1]:Tree[rt<<1|1];
		int x1=Tree[rt<<1].x,y1=Tree[rt<<1].y,x2=Tree[rt<<1|1].x,y2=Tree[rt<<1|1].y;
		ll ans;int l=Tree[rt<<1].l,r=Tree[rt<<1|1].r;
		if((ans=LCA.QueryLCA(x1,x2))>Tree[rt].dis) Tree[rt]=Node(x1,x2,ans);
		if((ans=LCA.QueryLCA(x1,y2))>Tree[rt].dis) Tree[rt]=Node(x1,y2,ans);
		if((ans=LCA.QueryLCA(y1,x2))>Tree[rt].dis) Tree[rt]=Node(y1,x2,ans);
		if((ans=LCA.QueryLCA(y1,y2))>Tree[rt].dis) Tree[rt]=Node(y1,y2,ans);
		Tree[rt].l=l;Tree[rt].r=r;
	}
	void Build(int l,int r,int rt){
		Tree[rt].l=l;Tree[rt].r=r;Tree[rt].len=r-l+1;
		if(l==r){
			Tree[rt].x=Tree[rt].y=LCA.RIdx[l];
			Tree[rt].dis=0;
			return ;
		}int mid=(l+r)>>1;
		Build(l,mid,rt<<1);Build(mid+1,r,rt<<1|1);
		PushUp(rt);
	}
	void Update(int ql,int qr,int rt){
		if(ql<=Tree[rt].l&&Tree[rt].r<=qr) return ;
		if(ql>=Tree[rt<<1|1].l) Update(ql,qr,rt<<1|1);
		else if(qr<=Tree[rt<<1].r) Update(ql,qr,rt<<1);
		else{
			Update(ql,qr,rt<<1);Update(ql,qr,rt<<1|1);
		}PushUp(rt);
	}
	void Show(int rt){
		printf("l=%d r=%d x=%d y=%d dis=%lld\n",Tree[rt].l,Tree[rt].r,Tree[rt].x,Tree[rt].y,Tree[rt].dis);
		if(Tree[rt].l==Tree[rt].r) return ;
		Show(rt<<1);Show(rt<<1|1);
	}
};
TreeDia Dia;
int U[MAXN],V[MAXN],Val[MAXN];
int n,m;

void Intt(){
	clean(Head,-1);Ecnt=0;
	clean(LCA.Deep,0);clean(LCA.Fa,-1);clean(LCA.Size,0);clean(LCA.Son,-1);
	LCA.Icnt=0;
}
void AddEdge(int u,int v,int val){
	Edge[Ecnt]=Edge1(v,val,Head[u]);
	Head[u]=Ecnt++;
}
int main(){
	Intt();
	scanf("%d",&n);
	for(int i=1;i<n;++i){
		scanf("%d%d%d",&U[i],&V[i],&Val[i]);
		AddEdge(U[i],V[i],Val[i]);AddEdge(V[i],U[i],Val[i]);
	}
	LCA.DFS1(1,-1,1);//找出深度 
	for(int i=1;i<n;++i){//按深度進行賦值,邊權跑到點權上 
		int u=U[i],v=V[i],val=Val[i];
		if(LCA.Deep[u]>LCA.Deep[v]) LCA.A[u]=Val[i];
		else LCA.A[v]=Val[i];
	}LCA.DFS2(1,1);//獲得DFS序 
	LCA.Build(1,n,1);//對DFS序進行剖分 
	Dia.Build(1,n,1);//獲得初始的序列 
	scanf("%d",&m);
	for(int i=1;i<=m;++i){
		char opt[5];scanf("%s",opt+1);
		if(opt[1]=='Q'){
			int x;scanf("%d",&x); 
			int u=Dia.Tree[1].x,v=Dia.Tree[1].y;
			ll dis1=LCA.QueryLCA(u,x),dis2=LCA.QueryLCA(v,x);
			printf("%lld\n",max(dis1,dis2));
		}
		else{
			int x,val;scanf("%d%d",&x,&val);
			int u=U[x],v=V[x];
			if(LCA.Deep[u]>LCA.Deep[v]) swap(u,v);
			LCA.Update(LCA.Idx[v],val,1);//維護更新後的LCA 
			Dia.Update(LCA.Idx[v],LCA.Idx[v]+LCA.Size[v]-1,1);//同時維護直徑線段樹 
		}
	}
}

/*
5
1 2 1
1 3 1
2 4 1
2 5 1
3
Q 1
C 1 2
Q 2
*/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章