• [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;
    }
  • 相关阅读:
    svn 更新
    首尾渐变
    sublime常用快捷键
    【CSS3】transition过渡和animation动画
    JS实现奇偶数的判断
    style、currentStyle、getComputedStyle区别介绍
    JavaScript中判断对象类型的种种方法
    CSS3 animation动画,循环间的延时执行时间
    EMCA创建em资料库时报错
    OS Kernel Parameter.semopm
  • 原文地址:https://www.cnblogs.com/floatiy/p/9478252.html
Copyright © 2020-2023  润新知