1. 程式人生 > 其它 >點分治本質與模板

點分治本質與模板

點分治

PS:零基礎可以學習模板題的第一篇題解,此隨筆僅做總結

本質:

利用基礎的分治思想,類似線段樹或者歸併,只不過是轉換到樹上。每次需要在當前子樹中找到一個點,這個點可以是重心,或者是滿足"每個子樹大小的最大值\(\leq tot/2\)"的某個點),選取這個點遞迴這個點的子樹,可以保證遞迴\(logn\)層。

隨後在每一層分類討論如何維護每個子樹歸併的答案貢獻(分治的主要思想),要用什麼樣的複雜度的演算法。如果每層用\(O(n)\)的做法,總複雜度就是\(O(nlogn)\)的。每層用\(O(nlogn)\)的做法,總複雜度就是\(O(nlog^2n)\)的。

點分治給我的感覺其實就是個思想,類似啟發式合併。

需要注意的點:

1.要標記一下重心被使用過,以免其餘子樹做操作的時候重複計算之前的重心。
2.注意討論輔助陣列是否在遞迴子樹前是否需要清0。

模板題目:P3806 【模板】點分治1

Code:

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
const int N=1e4+10;
struct node{
    int to;
    int next;
    int dis;
}edge[N<<1];
int head[N],num_edge,u,v,_d,base;
bool st[N];
bool vis[100000007];
void edge_add(int from,int to,int dis){
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    head[from]=num_edge;
}
int get_size(int u,int fa){
	if(st[u])return 0;
	int res=1;
	for(int i=head[u];i;i=edge[i].next){
		int to=edge[i].to;
		if(to==fa)continue;
		res+=get_size(to,u);
	}
	return res;
}
#define debug(x) cout<<#x<<" :"<<x<<endl
const int M=200;
struct node1{
	int val;
	int id;
}ask[M];

int ans[M];

int p[N];
int q[N];
int conp=0;
int conq=0;
int get_wc(int u,int fa,int tot,int &wc){
	if(st[u])return 0;
	int mx=0;
	int res=1;
	for(int i=head[u];i;i=edge[i].next){
		int to=edge[i].to;
		if(to==fa)continue;
		int val=get_wc(to,u,tot,wc);
		mx=max(mx,val);
		res+=val;
	}
	mx=max(mx,tot-res);
	if(mx<=tot/2){
		wc=u;
	}
	return res;
}


void get_dist(int u,int fa,int dist,int &con){
	if(st[u])return ;
	p[++con]=dist;
	for(int i=head[u];i;i=edge[i].next){
		int to=edge[i].to;
		if(to==fa)continue;
		get_dist(to,u,dist+edge[i].dis,con);
	}
}

void cal(int u){
	if(st[u])return ;
	get_wc(u,-1,get_size(u,-1),u);
	st[u]=true;

	//兩點在一個子樹中 遞迴做
	//一點在重心 一點在子樹
	//兩點在不同子樹中
	conq=0;
	for(int i=head[u];i;i=edge[i].next){

		int to=edge[i].to;

		get_dist(to,u,edge[i].dis,conp);
		//列舉一個點在當前子樹,另一個點是從前面所有子樹的內容轉移過來的
		for(int j=1;j<=conp;++j){
			for(int h=1;h<=m;++h){

				if(ask[h].val>=p[j]&&(!ans[ask[h].id])){
					if(vis[ask[h].val-p[j]]){
						ans[ask[h].id]=1;
					}
				}
			}	
		}

		for(int j=1;j<=conp;++j){//一個點在重心 一個點在該子樹
			vis[p[j]]=1;
			q[++conq]=p[j];
		}
		conp=0;
	}

	for(int i=1;i<=conq;++i){
		vis[q[i]]=false;//重置
	}

	for(int i=head[u];i;i=edge[i].next)cal(edge[i].to);
}

int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n-1;++i){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		edge_add(u,v,w);
		edge_add(v,u,w);
	}
	vis[0]=true;
	
	for(int i=1;i<=m;++i){
		
		scanf("%d",&ask[i].val);
		ask[i].id=i;
	}
	cal(1);

	for(int i=1;i<=m;++i){
		if(ans[ask[i].id])puts("AYE");
		else puts("NAY");
	}
	return 0;
}