動態點分治詳解

動態點分治大意:利用沒有樹結構修改的性質預處理重心樹優化時間

其實就是很暴力的思想,因爲樹的結構不變,所以每一次找到的重心都一樣,可以用一次點分樹預處理一下點分治的重心,再連接相鄰的重心便是點分樹。


前置知識:點分治

見另一篇blog:


算法:

好像就沒什麼了,上面就是全過程
詳細一點的分析:

  1. 找到當前子樹重心x
  2. 深搜x的每一個兒子的子樹,找到每一棵子樹的重心y(兩個重心的話隨便取一個)
  3. 對每一個y重複操作1和2
    在這裏插入圖片描述

這裏有一棵醜陋的樹,我們模擬一下建點分樹的過程,"-"表示深搜和連邊:

  • (1)
  • (1->3,5,8)
  • (5->2,9) (3->6,7) (8->4,13,14)
  • (2) (4) (6->10,11) (7->12) (9) (13) (14)
  • (10) (11) (12)

建出來長這樣:
在這裏插入圖片描述
這個東西明顯有兩個性質:

  1. 點分樹上的子樹必然包括原樹上的子樹(請讀者自行思考)
  2. 點分樹的樹高最大爲logn\log n(證明如下,其實也應該自己思考
    重心的每一棵子樹的大小都小於原樹的二分之一
    那麼每次至少都會把原樹劈成兩半
    我們最多需要logn\log n次把樹劈沒
    即樹高最大爲logn\log n,而且嚴重跑不滿(完全二叉警告

一般來說是不需要真的建出來的,拿一個fa記一下父親上跳就行,原因見下文。

對於大部分題都是求路徑,操作爲查詢和單點修改,根據點分治的性質,每次修改x只會影響到點分樹上根到x的點的權值,所以從下往上跳就行了。

這種問題一般不包括修改樹結構的操作,因爲一改全樹重心都會改變,屬於LCT問題,求大佬踩
對於有修改的題一般是在上一次答案的基礎上修改


模板:

#include<bits/stdc++.h>
using namespace std;
int e[1000010],nxt[1000010],head[100010],cnt;
int fa[100010],dis[100010],siz[100010];
bool vis[100010]int n,q,maxn=2e9,ss,len,rt; 
void add(int x,int y){
	cnt++;
	e[cnt]=y;
	nxt[cnt]=head[x];
	head[x]=cnt;
}
//-------------------------------------------點分樹 
void get_root(int x,int fa){
	siz[x]=1;
	int maxsiz=0;
	for(int i=head[x];i;i=nxt[i]){
		int y=e[i];
		if(y==fa||vis[y]) continue;
		get_root(y,x);
		siz[x]+=siz[y];
		if(siz[y]>maxsiz)
		maxsiz=siz[y];
	}
	if(ss-siz[x]>maxsiz)
	maxsiz=ss-siz[x];
	if(maxn>maxsiz){
		maxn=maxsiz;
		rt=x;
	} 
}
void get_dis(int x,int fat,int s){
	dis[++len]=s;
	for(int i=head[x];i;i=nxt[i]){
		int y=e[i];
		if(y==fat||vis[y]) continue;
		get_dis(y,x,s+1);
	}
}
void calc(int x,int s,int t){//計算答案
	len=0;
	get_dis(x,0,s);
	for(int i=1;i<=len;i++)
	//這裏視題目而定
} 
void dfs(int x){
	vis[x]=1;
	for(int i=head[x];i;i=nxt[i]){
		int y=e[i]; 
		if(vis[y]) continue; 
		maxn=2e9,rt=0,ss=siz[y];
		get_root(y,-1);
		calc(y,1,2);
		fa[rt]=x;
		dfs(rt);
	}
}
//------------------------------------------主程序 
int read(){
    char c=getchar(); int x=0,f=1;
    while(!isdigit(c) && c!='-') c=getchar();
    if(c=='-') { f=-1; c=getchar(); }
    while(isdigit(c)) { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
    return x*f;
}
int main(){
	int x,y;
	n=read();
	for(int i=1;i<n;i++){
		x=read(),y=read();
		add(x,y),add(y,x);
	}
	maxn=ss=n;
	get_root(1,0);
	dfs(rt); 
	q=read();
	for(int i=1;i<=q;i++){
	
	}
}
發佈了27 篇原創文章 · 獲贊 5 · 訪問量 2757
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章