学习笔记:点分治

时间:2021-08-17
本文章向大家介绍学习笔记:点分治,主要包括学习笔记:点分治使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

【题目】

  • P3806

  • 看了上面的题目,想必有很多种算法解决。

  • \(1\)、枚举不同的两个点,然后\(dfs\)算出之间的距离,判断一下就行了。时间复杂度大概是\(O(n^3)\)

  • \(2\)、选一个节点做树的根,求出每个点的深度。然后枚举两个点,求\(lca\),简单加减一下就行了。时间复杂度大概是\(O(n^2logn)\)

  • \(3\)、时间复杂度为\(O(nlogn)\)的算法点分治。


【点分治算法】

  • 点分治要我们每次选择一个点\(A\),处理有关点\(A\)的路径,然后将点\(A\)删除,然后重复以上过程。

第一步:找重心

  • 选择哪个点成为点\(A\)也是有学问的,选的不同会影响遍历的效率。
  • 显然选\(y\)比选\(x\)不优,选\(x\)最多递归\(2\)层,选\(y\)最多递归\(4\)层。

  • 所以,点\(A\)应该是当前子树的重心(重心所有的子树大小的最大值小于整个树大小的一半)。

第二步:求距离

  • 有关点\(A\)的路径无非就两种情况。

    • \(A\)为链首,点\(A\)的子树上另外一点为链尾
    • \(A\)其中一个子树上的点为链首,点\(A\)另外一棵子树上的点为链尾,绕过点\(A\)
  • 我们对重心的子树逐一处理\(\Longrightarrow\)就是先处理完一个子树,再处理另一个子树。

  • 先算出当前子树第\(i\)号节点到点\(A\)的距离\(d_i\),然后我们在桶里面查找\(k-d_i\),若找到了,便结束了。

  • 当前子树查找完了以后,便将\(d_i\)丢进桶里。

  • 切忌一边查找一边将\(d_i\)丢进桶里

  • 处理完所有子树,便在桶里查找\(k\),代表寻找子树中是否有一点与点\(A\)距离为\(k\)

第三步:递归

  • \(A\)的子树进行上述操作。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int INF=2147483647;
const int N=10100;
int n,m,b[N];
bool ans[N];
struct EDGE 
{ 
  int v,w,nx; 
}lb[N<<1]; 
int tot=1,top[N];
void add(int u,int v,int w)
{ 
  lb[++tot]=(EDGE){v,w,top[u]};
  top[u]=tot; 
}
bool vis[N];
int siz[N],masiz[N],q[N],he,ta;
void dfs0(int u)
{
	q[++ta]=u,vis[u]=1;
	siz[u]=1,masiz[u]=0;
	for(int i=top[u]; i; i=lb[i].nx)
	{
		int v=lb[i].v;
		if(vis[v]) continue;
		dfs0(v);
		siz[u]+=siz[v];
		masiz[u]=max(masiz[u],siz[v]);
	}
	vis[u]=0;
}
int d[N];
void dfs1(int u)
{
	q[++ta]=u,vis[u]=1;
	for(int i=top[u]; i; i=lb[i].nx)
	{
		int v=lb[i].v,w=lb[i].w;
		if(vis[v]) continue;
		d[v]=d[u]+w;
		dfs1(v);
	}
	vis[u]=0;
}
bool t[10000010];
void calc(int l,int r)
{
	for(int i=l; i<=r; i++)
	{
		int u=q[i];
		for(int j=1; j<=m; j++) 
		  if(b[j]>=d[u])ans[j]|=t[b[j]-d[u]];
	}
	for(int i=l; i<=r; i++) 
	  if(d[q[i]]<=1e7)t[d[q[i]]]=1;
}
void clear()
{
	for(int i=1; i<=ta; i++) 
	  if(d[q[i]]<=1e7)t[d[q[i]]]=0;
}
void solve(int u)
{
	he=1,ta=0,dfs0(u);
	int mi=INF,s=siz[u];
	for(int i=1; i<=ta; i++)
	{
		int v=q[i],g=max(masiz[v],s-siz[v]);
		if(g<mi) mi=g,u=v;
	}
	he=1,ta=0;
	d[u]=0,vis[u]=1;
	for(int i=top[u]; i; i=lb[i].nx)
	{
		int v=lb[i].v,w=lb[i].w;
		if(vis[v])continue;
		d[v]=d[u]+w;
		dfs1(v);
		calc(he,ta);
		he=ta+1;
	}
	q[++ta]=u;
	calc(he,ta);
	clear();
	for(int i=top[u]; i; i=lb[i].nx)
	{
		int v=lb[i].v;
		if(vis[v]) continue;
		solve(v);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1; i< n; i++)
	{
		int x,y,w; 
		scanf("%d%d%d",&x,&y,&w);
		add(x,y,w),add(y,x,w);
	}
	for(int i=1; i<=m; i++)scanf("%d",&b[i]);
	solve(1);
	for(int i=1; i<=m; i++)
	  if(ans[i])printf("AYE\n");
	  else printf("NAY\n");
    return 0;
}

原文地址:https://www.cnblogs.com/zeromclai/p/15152591.html