[点分治]luogu P3806 【模板】点分治1

题目描述

给定一棵有n个点的树

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

输入输出格式

输入格式:

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

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

输出格式:

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

输入输出样例

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

说明

对于30%的数据n<=100

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

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

分析

其实点分治的主体思想也就是对于树的重心进行分治,也就是找到重心以后,再找子树的重心,这样效率最大化

分治的内容千变万化,只有熟练才能分析出来(常用的有容斥原理)

#include <iostream> 
#include <cstdio>
using namespace std;
const int N=1e5+10;
struct Edge {
    int u,v,w,nx;
}g[N];
int list[N],cnt;
int sz[N],bg[N],rt,p[N],pcnt,cd[10000010],d[N];
bool b[N];
int n,m;

void Add(int u,int v,int w) {
    g[++cnt]=(Edge){u,v,w,list[u]};list[u]=cnt;
}

void Get_Root(int u,int fa) {
    sz[u]=1;bg[u]=0;
    for (int i=list[u];i;i=g[i].nx)
        if (g[i].v!=fa&&!b[g[i].v]) {
            Get_Root(g[i].v,u);
            sz[u]+=sz[g[i].v];
            bg[u]=max(bg[u],sz[g[i].v]);
        }
    bg[u]=max(bg[u],n-bg[u]);
    if (bg[u]<bg[rt]) rt=u;
}

void Get_Dist(int u,int fa,int len) {
    p[++pcnt]=len;
    for (int i=list[u];i;i=g[i].nx) 
        if (g[i].v!=fa&&!b[g[i].v]) Get_Dist(g[i].v,u,len+g[i].w);
}

void Calc(int u,int len,int add) {
    pcnt=0;
    Get_Dist(u,0,len);
    for (int i=2;i<=pcnt;i++)
        for (int j=1;j<i;j++) cd[p[i]+p[j]]+=add;
}

void Solve(int u) {
    b[u]=1;
    Calc(u,0,1);
    for (int i=list[u];i;i=g[i].nx)
        if (!b[g[i].v]) {
            Calc(g[i].v,g[i].w,-1);
            rt=0;Get_Root(g[i].v,u);
            Solve(rt);
        }
}

int main() {
    scanf("%d%d",&n,&m);
    for (int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),Add(u,v,w),Add(v,u,w);
    bg[0]=2147483647;
    Get_Root(1,0);
    Solve(rt);
    for (int i=1,k;i<=m;i++) {
        scanf("%d",&k);
        printf(!cd[k]?"NAY
":"AYE
");
    }
}
View Code
在日渐沉没的世界里,我发现了你。
原文地址:https://www.cnblogs.com/mastervan/p/10208219.html