点分治

可能有一些点分治的模板是\(O(n^2)\)的,但是如果在每次计算时都重新计算一下当前枚举子树的重心,就可以使得复杂度为\(O(nlogn)\)

点分治能处理的问题:在树上对具有某些限定条件路径进行静态统计的算法。 当然如果要待修之类的话紫荆花之恋是一道不错的题,不刻意压行大概只需要写250行QAQ

首先我们定一个顶点(怎么定等会儿说) 显然树上的路径分为两种:经过顶点的和不经过顶点的。令\(q[i]\)表示节点\(i\)到这个顶点的距离。显然经过顶点的路径\(i,j\)长度为\(q[i]+q[j]\) 如果不经过顶点:我们可以在子树里面再找一个顶点,使得路径经过该顶点 这样又可以用经过顶点的公式来算了(但是显然需要重新计算\(q\))

现在回到如何定一个顶点。如果我们令当前顶点的儿子为子树节点,如果是一条链的话,会慢到吃粑粑 为了使得树的儿子分布更均匀,我们可以考虑用当前子树的重心作为子树的顶点 这样递归深度不会超过\(logn\)层,因此时间复杂度也就保证了\(O(nlogn)\)

解决了分治之后,显然这道题就是一道水题了,然后就可以(对于大佬来说)秒切了 (可是我是蒟蒻,所以我不能秒切)

我们用\(q[i]\) 来储存以当前枚举的顶点的子树的\(dis\)\(judge[x]\)存储以当前顶点的子树中是否有长度为\(x\)的边 将询问离线一下(就差不多完了)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=100010;
const int inf=1e9;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,m,tot,fir[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1];
bool visit[maxn],judge[maxn],ans[maxn];
int size[maxn],root,maxx[maxn],shawn[maxn],mendes,sum,ques[maxn],dis[maxn];
int q[maxn],tail,qaq;
void add(int x,int y,int z){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;val[tot]=z;}
void getroot(int x,int fa){
	size[x]=1;int num=0;
	for(int i=fir[x];i;i=nxt[i]){
		int y=to[i];if(y==fa || visit[y]) continue;
		getroot(y,x);size[x]+=size[y];
		maxx[x]=max(maxx[x],size[y]);
	}
	maxx[x]=max(maxx[x],qaq-size[x]);
	if(maxx[x]<maxx[root]) root=x;
}
void calc_dis(int x,int fa){
	q[++tail]=dis[x];
	for(int i=fir[x];i;i=nxt[i]){
		int y=to[i];if(y==fa || visit[y]) continue;
		dis[y]=dis[x]+val[i];calc_dis(y,x);
	}
}
void work(int x){
	int mendes=0;sum=0;
	for(int i=fir[x];i;i=nxt[i]){
		int y=to[i];if(visit[y]) continue;
		tail=0;dis[y]=val[i];calc_dis(y,x);
		for(int j=1;j<=tail;j++){
			for(int k=1;k<=m;k++){
				if(ques[k]>=q[j]){
					ans[k]|=judge[ques[k]-q[j]];
				}
			}
		}
		for(int j=1;j<=tail;j++){
			judge[q[j]]=true;shawn[++mendes]=q[j];
		}
	}
	for(int i=1;i<=mendes;i++) judge[shawn[i]]=false;
}
void solve(int x){
	visit[x]=true;judge[0]=true;
	work(x);
	for(int i=fir[x];i;i=nxt[i]){
		int y=to[i];if(visit[y]) continue;
		maxx[root=0]=inf;qaq=size[y];
		getroot(y,x);solve(root);
	}
}
int main(){
	n=read();m=read();
	for(int i=1,x,y,z;i<=n-1;i++){
		x=read();y=read();z=read();
		add(x,y,z);add(y,x,z);
	}
	for(int i=1;i<=m;i++) ques[i]=read();
	maxx[0]=n;qaq=n;getroot(1,0);
	solve(root);
	for(int i=1;i<=m;i++)
		ans[i]?printf("AYE\n"):printf("NAY\n");
	return 0;
}
posted on 2019-11-08 19:29  萌德真帅  阅读(95)  评论(0编辑  收藏  举报