淀粉质学习笔记

前言

淀粉质是一种用来解决两两点对之间类似路径之类的问题。通过选取重心,使得暴力求解更加的快速。

具体思路

淀粉质通常是通过选取重心的方式,将一棵树不断分治成比较均匀的几部分。因为重心的性质,最多只会递归 \(\log n\) 层。假设每一层的时间复杂度都是 n 的话,那么总的就是 \(n \log n\) 的。

所以,淀粉质的写法就是先对于每个处理的子树,先找到他的重心,然后暴力处理他的子树。然后再进入他的子树中分治处理。注意进入他的子树后,重心这个点就相当于被删除了。

一些注意事项

很多的淀粉质写法中计算重心时都会写 sum = size[v] 。但我们一思考会发现如果当前计算子树的时候是以当前节点的父节点来计算的话,进入重心的父节点的时候,sum并不是其子树大小。但其实这也是正确的,因为如果是父节点的话,下一次sum就会正确,所有最多层数也只会乘以2。具体的证明见这篇博客

但是这样的确会是层数变的更深,如果一些题对层数有要求的话,就要写正确的做法,每次多少一遍求出重心为根的时候子树的大小。

code

#include<bits/stdc++.h>
using namespace std;
const int N=10000011;
int n,m;
int k[N];
#define inf 1000000000
struct node{
	int to,lt,val;
}e[N<<1];
int tot,last[N];
void add(int u,int v,int w){
	e[++tot].lt=last[u];
	e[tot].to=v;
	e[tot].val=w;
	last[u]=tot;
}
int rt;
int size[N],sum,maxp[N];
int vis[N],cnt;
void getrt(int u,int fa){
	size[u]=1;maxp[u]=0;
	for(int i=last[u];i;i=e[i].lt){
		int v=e[i].to;
		if(v==fa||vis[v])continue;
		getrt(v,u);
		size[u]+=size[v];
		maxp[u]=max(maxp[u],size[v]);
	}
	maxp[u]=max(maxp[u],sum-size[u]);
	if(maxp[u]<maxp[rt])rt=u;
}
int dis[N],ins[N],injude[10000011];
void getdis(int u,int fa){
	ins[++cnt]=dis[u];
	for(int i=last[u];i;i=e[i].lt){
		int v=e[i].to;
		if(v==fa||vis[v])continue;
		dis[v]=dis[u]+e[i].val;
		getdis(v,u);
	}
}
int q[N];
int test[N];
void clac(int u){
	int tail=0;
	for(int i=last[u];i;i=e[i].lt){
		int v=e[i].to;
		if(vis[v])continue;
		dis[v]=e[i].val;
		cnt=0;
		getdis(v,u);
		for(int j=cnt;j>=1;j--){
			for(int t=1;t<=m;t++){
				if(k[t]>=ins[j])
					if(injude[k[t]-ins[j]]==1)test[t]=1;
			}
		}
		for(int j=cnt;j>=1;j--)
			injude[ins[j]]=1,q[++tail]=ins[j];
	}
	for(int i=1;i<=tail;i++)
		injude[q[i]]=0;
}
void slove(int u){
	vis[u]=injude[0]=1;
	clac(u);
	for(int i=last[u];i;i=e[i].lt){
		int v=e[i].to;
		if(vis[v])continue;
		rt=0;
		sum=size[v];
		maxp[rt]=inf;
		getrt(v,0);
		slove(rt);
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n-1;i++){
		int u,v,w;
		cin>>u>>v>>w;
		add(u,v,w);
		add(v,u,w);
	}
	for(int i=1;i<=m;i++){
		cin>>k[i];
	}
	maxp[0]=sum=n;
	getrt(1,0);
//	cout<<rt<<endl;
	slove(rt);
	for(int i=1;i<=m;i++)
		if(test[i]==1)cout<<"AYE"<<endl;
		else cout<<"NAY"<<endl;
}

posted @ 2023-08-10 10:27  shAdomOvO  阅读(67)  评论(0)    收藏  举报