点分治 (模板)

题目描述

给定一棵有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

扫描二维码关注公众号,回复: 1576846 查看本文章

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

题解

刚刚学了点分治,感觉其实不是很难,处理关于树上两点之间的路径的问题,思想是每次寻找树的重心,
之后开始不断向子树递归。

代码


#include<iostream>
#include<cstdio>

using namespace std;
const int MAXN = 100010;

int n,m,head[MAXN],cnt,tt;
int pp;     //以同一个点为根的子树编号 
int sum;     //子树的总点数 
int rt;     //当前的子树的根 
int d[MAXN];   //i到当前根的距离  
int f[MAXN],son[MAXN];  //f为除去根得到的最大联通块,son为以i为根的子树的节点 
bool vis[MAXN],ko[10000005];  //k是否存在 

struct Edge{
    int nxt,to,w;
}edge[MAXN*2];

struct Node{
    int dis,which;
}node[MAXN];

inline void add(int bg,int ed,int v){
    edge[++cnt].to=ed;
    edge[cnt].nxt=head[bg];
    edge[cnt].w=v;
    head[bg]=cnt;
}

inline void getroot(int u,int fa){  //找重心 
    son[u]=1;f[u]=0;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(vis[v] || v==fa) continue;
        getroot(v,u);
        son[u]+=son[v];
        f[u]=max(f[u],son[v]);
    }
    f[u]=max(f[u],sum-son[u]);
    rt=f[u]<f[rt]?u:rt;
}

inline void getdeep(int rearoot,int u,int fa,int ro){  //得到点到根的距离。 
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(vis[v] || v==fa) continue;
        d[v]=d[u]+edge[i].w;
        ko[d[v]]=1; 
        if(u==rearoot) {
            node[++tt].dis=d[v];
            node[tt].which=++pp;
            getdeep(rearoot,v,u,pp);
        } 
        else{
            node[++tt].dis=d[v];
            node[tt].which=ro;
            getdeep(rearoot,v,u,ro);
        }

    }
}

inline void getans(int u){
    d[u]=0;tt=0;pp=0;
    getdeep(u,u,0,0);
    vis[u]=1;
    for(register int i=1;i<=tt;i++) 
        for(register int j=i+1;j<=tt;j++)
            if(node[i].which!=node[j].which)
                ko[node[i].dis+node[j].dis]=1;
    for(register int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(vis[v]) continue;
        rt=0;sum=son[v];
        getroot(v,0);
        getans(rt);
    }
}

int main(){
    scanf("%d%d",&n,&m);
    for(register int i=1;i<n;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);add(b,a,c);
    }
    sum=f[0]=n;
    rt=0;
    getroot(1,0);  //找重心。 
    getans(rt);  //以重心建树。 
    for(register int i=1;i<=m;i++){
        int kk;
        scanf("%d",&kk);
        if(ko[kk]==1) printf("AYE\n");
        else printf("NAY\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40448823/article/details/80668460