[Luogu] 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<=1000,K<=10000000

题目解析

很有必要好好讲讲点分治,很有意思的算法。

对于一些树上的询问(如本题的路径长度),直接进行计算可能带来巨大的复杂度。

如果了解分治的话应该知道我们可以利用分治来降低一些东西的复杂度。

说白了,树上分治分为点分治和边分治。点分治就是一种树上的分治算法。

就这道题而言,选取一个点为重心,所有路径只有两种情况:直接经过这个点或在这个点的子树中。这时我们处理直接经过该点的路径,同时进行分治,递归处理子树。

但是有一个问题:这样的算法可能被某些极端图(链,菊花图等)卡到时间爆炸,我们必须采取对策——找子树的重心作为新的选取点,这样就可以了

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXK = 10000000 + 5;
const int MAXM = 100 + 5;
const int MAXN = 10000 + 5;

int n,m;
int sum,root;
struct Edge {
    int nxt;
    int to,w;
} l[MAXN << 1];
int cnt,head[MAXN];
int maxpart[MAXN],siz[MAXN];
int dis[MAXN],rem[MAXN],q[MAXN];
int test[MAXK],judge[MAXK];
int query[MAXM];
bool vis[MAXN];

void add(int x,int y,int z) {
    cnt++;
    l[cnt].nxt = head[x];
    l[cnt].to = y;
    l[cnt].w = z;
    head[x] = cnt;
    return;
}

void findroot(int x,int from) {
    siz[x] = 1;
    for(int i = head[x]; i; i = l[i].nxt) {
        if(l[i].to == from || vis[l[i].to]) continue;
        findroot(l[i].to,x);
        siz[x] += siz[l[i].to];
        maxpart[x] = max(maxpart[x],siz[l[i].to]);
    }
    maxpart[x] = max(maxpart[x],sum - siz[x]);
    if(maxpart[x] < maxpart[root]) root = x;
    return;
}

void getdis(int x,int from) {
    rem[++rem[0]] = dis[x];
    for(int i = head[x]; i; i = l[i].nxt) {
        if(l[i].to == from || vis[l[i].to]) continue;
        dis[l[i].to] = dis[x] + l[i].w;
        getdis(l[i].to,x);
    }
    return;
}

void calc(int x) {
    int tmp = 0;
    for(int i = head[x]; i; i = l[i].nxt) {
        if(vis[l[i].to]) continue;
        rem[0] = 0;
        dis[l[i].to] = l[i].w;
        getdis(l[i].to,x);
        for(int j = rem[0]; j; j--) {
            for(int k = 1; k <= m; k++) {
                if(query[k] >= rem[j] && judge[query[k] - rem[j]]){
                    test[k] = true;
                }
            }
        }
        for(int j = rem[0]; j; j--) {
            q[++tmp] = rem[j];
            judge[rem[j]] = true;
        }
    }
    for(int i = 1; i <= tmp; i++) {
        judge[q[i]] = false;
    }
    return;
}

void solve(int x) {
    vis[x] = judge[0] = 1;
    calc(x);
    for(int i = head[x]; i; i = l[i].nxt) {
        if(vis[l[i].to])continue;
        sum = siz[l[i].to];
        root = 0;
        findroot(l[i].to,0);
        solve(root);
    }
}

int main() {
    scanf("%d%d",&n,&m);
    int x,y,z;
    for(int i = 1; i < n; i++) {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    for(int i = 1; i <= m; i++) {
        scanf("%d",&query[i]);
    }
    maxpart[root] = sum = n;
    findroot(1,0);
    solve(root);
    for(int i = 1; i <= m; i++) {
        if(test[i]) printf("AYE
");
        else printf("NAY
");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/floatiy/p/9478252.html