📚【模板】点分治

点分治用来解决一类同技术上路径的问题。

点分治主要的思想就是:

  1. 找一个点,并统计经过这个点的路径;

  2. 删除这个点,继续查询它的子树。

实际上每次找这个分治的点,多半选的是重心,这与正确性无关,但与时间有关。

当然还有一个重头就是统计。

模板题:\(\text{luogu P3806 【模板】点分治1}\)

  1. 首先,要查找重心:
  • \(size_i\):以\(i\)点为根的树的大小;

  • \(weight_i\):点\(i\)的权重(子树最大大小);

  • \(totalpoint\):现在所在的树的大小;

  • \(centroid\):找到的重心。

int size[N], weight[N], totalpoint, centroaid;
void get_centroaid(int u,int p) {
	size[u] = 1;
	weight[u] = 0;
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		if(v == p||visited[v]) 
			continue;
		get_centroaid(v,u);
		size[u] += size[v];
		weight[u] = std :: max(weight[u],size[v]);
	}
	weight[u] = std :: max(weight[u],totalpoint-size[u]);
	if(!centroaid||weight[u] < weight[centroaid]) 
		centroaid = u;
}
  1. 然后要开始递归
  • \(visited_i\):点\(i\)是否被删除。
//in signed main();
weight[0] = n;
totalpoint = n;
get_centroaid(1,0);
solve(centroaid);
//void solve();
void solve(int u) {
	visited[u] = true;
	calculate(u);
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		if(visited[v]) 
			continue;
		centroaid = 0;
		totalpoint = size[v];
		get_centroaid(v,0);
		solve(centroaid);
	}
}

很明显就是找到一个点,开始统计,然后继续对子树进行统计。

  1. 统计
  • \(point\left[\right]\):目前树上的点;

  • \(dis_i\):点\(i\)到现在选取的根节点的距离;

  • \(belong_i\):点\(i\)属于哪棵子树。

int point[N], dis[N], belong[N], point_tot;
bool comp(const int &_x,const int &_y) {
	return dis[_x] < dis[_y];
}
void get_dis(int u,int p,int dist,int ancestor) {
	dis[point[++point_tot] = u] = dist;
	belong[u] = ancestor;
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		if(v == p||visited[v]) 
			continue;
		get_dis(v,u,dist+edge[i].w,ancestor);
	}
}
void calculate(int u) {
	point[point_tot = 1] = u;
	dis[belong[u] = u] = 0;
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		if(visited[v]) 
			continue;
		get_dis(v,u,edge[i].w,v);
	}
	std :: sort(point+1,point+point_tot+1,comp);
	for(int i = 1;i <= m;++i) {
		int l = 1, r = point_tot;
		if(check_point[i]) 
			continue;
		while(l < r) {
			if(dis[point[l]]+dis[point[r]] > query[i]) 
				--r;
			else if(dis[point[l]]+dis[point[r]] < query[i]) 
				++l;
			else if(belong[point[l]] == belong[point[r]]) {
				if(dis[point[r]] == dis[point[r-1]]) 
					--r;
				else 
					++l;
			} else {
				check_point[i] = true;
				break;
			}
		}
	}
}

对于以\(centroid\)为根节点的树,统计完距离之后,依次翻看每一个问题,用两个指针\(l\)\(r\)遍历数组。在距离不正确或同属一颗子树时跳指针。如果合法就打上标记。

复杂度是\(\operatorname{O}(n\log^2 n+nm\log n)\)

code here
#include <stdio.h>
#include <bits/stl_algobase.h>
#include <bits/stl_algo.h>

const int N = 10010;
const int M = 110;

struct EDGE {
	int t, next;
	int w;
} edge[N<<1];
int edge_tot, head[N];
void add_edge(int f,int t,int w) {
	edge[++edge_tot].next = head[f];
	edge[edge_tot].t = t;
	edge[edge_tot].w = w;
	head[f] = edge_tot;
}

int n, m, query[M];
bool check_point[M];

bool visited[N];

int size[N], weight[N], totalpoint, centroaid;
void get_centroaid(int u,int p) {
	size[u] = 1;
	weight[u] = 0;
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		if(v == p||visited[v]) 
			continue;
		get_centroaid(v,u);
		size[u] += size[v];
		weight[u] = std :: max(weight[u],size[v]);
	}
	weight[u] = std :: max(weight[u],totalpoint-size[u]);
	if(!centroaid||weight[u] < weight[centroaid]) 
		centroaid = u;
}

int point[N], dis[N], belong[N], point_tot;
bool comp(const int &_x,const int &_y) {
	return dis[_x] < dis[_y];
}
void get_dis(int u,int p,int dist,int ancestor) {
	dis[point[++point_tot] = u] = dist;
	belong[u] = ancestor;
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		if(v == p||visited[v]) 
			continue;
		get_dis(v,u,dist+edge[i].w,ancestor);
	}
}
void calculate(int u) {
	point[point_tot = 1] = u;
	dis[belong[u] = u] = 0;
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		if(visited[v]) 
			continue;
		get_dis(v,u,edge[i].w,v);
	}
	std :: sort(point+1,point+point_tot+1,comp);
	for(int i = 1;i <= m;++i) {
		int l = 1, r = point_tot;
		if(check_point[i]) 
			continue;
		while(l < r) {
			if(dis[point[l]]+dis[point[r]] > query[i]) 
				--r;
			else if(dis[point[l]]+dis[point[r]] < query[i]) 
				++l;
			else if(belong[point[l]] == belong[point[r]]) {
				if(dis[point[r]] == dis[point[r-1]]) 
					--r;
				else 
					++l;
			} else {
				check_point[i] = true;
				break;
			}
		}
	}
}

void solve(int u) {
	visited[u] = true;
	calculate(u);
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		if(visited[v]) 
			continue;
		centroaid = 0;
		totalpoint = size[v];
		get_centroaid(v,0);
		solve(centroaid);
	}
}

signed main() {
	scanf("%d %d",&n,&m);
	for(int i = 1, f, t, w;i < n;++i) {
		scanf("%d %d %d",&f,&t,&w);
		add_edge(f,t,w);
		add_edge(t,f,w);
	}
	for(int i = 1;i <= m;++i) {
		scanf("%d",&query[i]);
		if(!query[i]) 
			check_point[i] = true;
	}
	weight[0] = n;
	totalpoint = n;
	get_centroaid(1,0);
	solve(centroaid);
	for(int i = 1;i <= m;++i) 
		printf("%s\n",check_point[i] ? "AYE" : "NAY");
}
posted @ 2022-08-10 22:13  bikuhiku  阅读(19)  评论(0编辑  收藏  举报