点分治本质与模板

点分治

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;
}
posted @ 2021-06-15 21:21  Qquun  阅读(64)  评论(0)    收藏  举报