点分治

前言

WARNING:代码有点问题!但是懒得改了!

好久没见过这么优雅的暴力了,这不学后悔一生啊!

说实话,还不太会。

讲解

其实就是改变一下树的遍历顺序,然后做到 (O(nlogn)) 的时间复杂度

大概的思路是:

对于一棵树,我们找到它的重心,以其为根向下延伸,然后对于它的子树我们也做这样的操作,找到它们的重心作为其新的儿子。由于我们改变了遍历顺序,所以我们要用 (vis) 数组将已经经过的点打上标记。

经典操作是对于每个点,我们可以遍历其整棵子树,然后用一些操作更新答案,通常我们见到的操作可以用 数据结构 或者 two pointers 更新。这样的时间复杂度是正确的 (O(nlogn)),多么优雅的暴力,不是吗。

具体的可以看板题代码。

练习

板题(洛谷)

代码

板题代码。

int head[MAXN],tot;
struct edge
{
	int v,w,nxt;
}e[MAXN << 1];
void Add_Edge(int x,int y,int z)
{
	e[++tot].v = y;
	e[tot].w = z;
	e[tot].nxt = head[x];
	head[x] = tot;
}
void Add_Double_Edge(int x,int y,int z)
{
	Add_Edge(x,y,z);
	Add_Edge(y,x,z);
}

bool vis[MAXN];
int siz[MAXN],MAX[MAXN];
void getrt(int x,int fa,int S)//找重心
{
	siz[x] = 1;
	MAX[x] = 0;
	for(int i = head[x]; i ;i = e[i].nxt)
	{
		if(e[i].v == fa || vis[e[i].v]) continue;
		getrt(e[i].v,x,S);
		siz[x] += siz[e[i].v];
		MAX[x] = Max(MAX[x],siz[e[i].v]);
	}
	MAX[x] = Max(MAX[x],S-siz[x]);
	if(!rt || MAX[x] < MAX[rt]) rt = x; 
}
int cnt;
int p[MAXN],bl[MAXN],dis[MAXN];
void getdis(int x,int fa,int D,int from) 
{
	p[++cnt] = x;
	bl[x] = from;
	dis[x] = D;
	for(int i = head[x]; i ;i = e[i].nxt)
		if(e[i].v != fa && !vis[e[i].v])
			getdis(e[i].v,x,D+e[i].w,from);
}
bool cmp(int x,int y){return dis[x] < dis[y];}
void update(int x)
{
	cnt = 0;
	p[++cnt] = x;
	bl[x] = x;
	dis[x] = 0;
	for(int i = head[x]; i ;i = e[i].nxt)
		if(!vis[e[i].v]) getdis(e[i].v,x,e[i].w,e[i].v);
	sort(p+1,p+cnt+1,cmp);
	for(int i = 1;i <= Q;++ i)
	{
		if(aye[i]) continue;
		int l = 1,r = cnt;
		while(l < r)
		{
			if(dis[p[l]] + dis[p[r]] < q[i]) l++;
			else if(dis[p[l]] + dis[p[r]] > q[i]) r--;
			else if(bl[p[l]] == bl[p[r]]) 
			{
				if(bl[p[r]] == bl[p[r-1]]) r--;
				else l++;
			}
			else 
			{
				aye[i] = 1;
				break;
			}
		}
	}
}
void solve(int x)
{
	vis[x] = 1;
        //应该在这个位置重新跑一遍dfs更新siz
	update(x);
	for(int i = head[x]; i ;i = e[i].nxt)
	{
		if(vis[e[i].v]) continue;
		rt = 0;//记得初始化!
		getrt(e[i].v,x,siz[e[i].v]);//这里的siz没更新,有点问题
		solve(rt);
	}
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read(); Q = Read();
	for(int i = 1,u,v;i < n;++ i) u = Read(),v = Read(),Add_Double_Edge(u,v,Read());
	for(int i = 1;i <= Q;++ i)
	{
		q[i] = Read();
		if(!q[i]) aye[i] = 1;
	}
	getrt(1,0,n);
	solve(rt);
	for(int i = 1;i <= Q;++ i)
	{
		if(aye[i]) printf("AYE
");
		else printf("NAY
");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/PPLPPL/p/14407345.html