点分治 模板

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/84639042

https://www.luogu.org/problemnew/show/P3806

#include<bits/stdc++.h>
using namespace std;
const int inf=10000000;
const int maxn=100010;
inline int read()
{
	int f=1,num=0;
	char ch=getchar();
	while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); }
	while (isdigit(ch)) num=(num<<1)+(num<<3)+(ch^48), ch=getchar();
	return num*f;
}
int ans,n,m;
int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],head[maxn],tot=0;
void add(int x,int y,int z)
{
   ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
int sum,root;
int maxp[maxn],size[maxn],vis[maxn];
//sum是当前子树的总结点数,size[x]是以x为根的子树大小
void getRoot(int x,int fa)
{
    size[x]=1;
	maxp[x]=0;
    for(int i=head[x];i;i=Next[i]) 
    {
        int y=ver[i];
        if(y==fa||vis[y]) continue;
        getRoot(y,x);//先递归得到子树大小
        size[x]+=size[y];
        maxp[x]=max(maxp[x],size[y]);//更新x结点的maxp
    }
    maxp[x]=max(maxp[x],sum-size[x]);
    if(maxp[x]<maxp[root]) root=x;//更新当前子树的重心
}

int dis[maxn],rem[maxn];
void getEdge(int x,int fa)
{
    rem[++rem[0]]=dis[x];
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==fa||vis[y]) continue;
        dis[y]=dis[x]+edge[i];
        getEdge(y,x);
    }
}

int query[1010];
int test[inf],judge[inf],q[maxn];
void calc(int x)
{
    int p=0;
    for(int i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(vis[y]) continue;
        rem[0]=0;
		dis[y]=edge[i];
        getEdge(y,x);//处理u的每个子树的dis

        for(int j=rem[0];j;--j)//遍历当前子树的dis
        	for(int k=1;k<=m;++k)//遍历每个询问
        		if(query[k]>=rem[j])
        			test[k]|=judge[query[k]-rem[j]];
        //如果query[k]-rem[j]d的路径存在就标记第k个询问

        for(int j=rem[0];j;--j)//保存出现过的dis于judge
        	q[++p]=rem[j],judge[rem[j]]=1;
    }
    for(int i=1;i<=p;++i)//处理完这个子树就清空judge
    	judge[q[i]]=0;//特别注意一定不要用memeset,会T

}

void solve(int x)
{   
    //judge[i]表示到根距离为i的路径是否存在
    vis[x]=judge[0]=1;
	calc(x);//处理以u为根的子树
    for(int i=head[x];i;i=Next[i])//对每个子树进行分治
    {
        int y=ver[i];
        if(vis[y])continue;
        sum=size[y];
		maxp[root=0]=inf;
        getRoot(y,0);
		solve(root);//在子树中找重心并递归处理
    }
}

int main()
{
    n=read(),m=read();
    for(int i=1;i<n;++i)
    {
        int x=read(),y=read(),z=read();
        add(x,y,z),add(y,x,z);
    }
    for(int i=1;i<=m;++i)
    	query[i]=read();//先记录每个询问以离线处理

    maxp[root]=sum=n;//第一次先找整棵树的重心
    getRoot(1,0); 
    solve(root);//对树进行点分治

    for(int i=1;i<=m;++i)
    {
        if(test[i]) printf("AYE\n");
        else printf("NAY\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/84639042