P3806 【模板】点分治1

题目背景

感谢hzwer的点分治互测。

题目描述

给定一棵有n个点的树

询问树上距离为k的点对是否存在。

输入输出格式

输入格式:

n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径

接下来m行每行询问一个K

输出格式:

对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号)

输入输出样例

输入样例#1: 
2 1
1 2 2
2
输出样例#1: 
AYE

说明

对于30%的数据n<=100

对于60%的数据n<=1000,m<=50

对于100%的数据n<=10000,m<=100,c<=10000,K<=10000000

代码

对于每个点,分为有长度为k的路径经过点以及长度为k的路径在其子树

对于长度为k的路径在其子树的点,我们可以将其看作是长度为k的路径经过其子树所在根节点

那么这就是点分治的基本思想

为降低复杂度,我们每次采取重心作为根节点。将树的深度将至logn

则复杂度为O(NlogN2)

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=10000+100,maxm=10000000+100;
int head[maxn];
int q[maxn],ans[maxn];
int p[maxm],t[maxn];
int d[maxn],dis[maxn],vis[maxn];
int s[maxn],ms[maxn];
int rt=0,sum;
int n,m;
struct edge
{
    int to,next,val;
}e[maxn<<1];
int size=0;
inline 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<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
void addedge(int u,int v,int w)
{
    e[++size].to=v;e[size].val=w;e[size].next=head[u];head[u]=size;
}
void tc(int u,int fa)//树的重心 
{
    s[u]=1;ms[u]=0;
    for(int i=head[u];i;i=e[i].next)
    {
        int to=e[i].to;
        if(to==fa||vis[to])continue;
        tc(to,u);
        s[u]+=s[to];
        ms[u]=max(ms[u],s[to]);
    }
    ms[u]=max(ms[u],sum-ms[u]);
    if(ms[u]<ms[rt])rt=u;
}
void dfs(int u,int fa)
{
    d[++d[0]]=dis[u];
    for(int i=head[u];i;i=e[i].next)
    {
        int to=e[i].to;
        if(to==fa||vis[to])continue;
        dis[to]=dis[u]+e[i].val;
        dfs(to,u);
    }
}
void cal(int u)
{
    int tot=0;
    for(int i=head[u];i;i=e[i].next)
    {
        int to=e[i].to;
        if(vis[to])continue;
        d[0]=0;dis[to]=e[i].val;
        dfs(to,u);
        for(int j=1;j<=d[0];++j)
        for(int k=1;k<=m;++k)
        if(!ans[k]&&q[k]>=d[j])//有长度为q[k]的路径:该子树与之前其他子树距离和为q[k]
        ans[k]=p[q[k]-d[j]];
        for(int j=1;j<=d[0];++j)
        t[++tot]=d[j],p[d[j]]=1;//用桶存下到该节点已有的距离 
    }
    for(int i=1;i<=tot;i++)
    p[t[i]]=0;//桶清空,用memsetTLE 
}
void solve(int u)
{
    vis[u]=p[0]=1;cal(u);
    for(int i=head[u];i;i=e[i].next)
    {
        int to=e[i].to;
        if(vis[to])continue;
        sum=s[to],ms[rt=0]=inf;
        tc(to,0);solve(rt);
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read(),w=read();
        addedge(u,v,w);
        addedge(v,u,w);
    }
    for(int i=1;i<=m;i++)
    q[i]=read();
    sum=n;ms[rt]=inf;
    tc(1,0);
    solve(rt);
    for(int i=1;i<=m;i++)
    puts(ans[i]?"AYE":"NAY");
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/DriverBen/p/10963438.html