• [cdq分治][树的重心] 洛谷 P3806 点分治1


    题目描述

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

    代码

    • 对于一条x到y的路径有可能有两种情况,
    • ①要经过根节点,那么这样的距离就是dis[u]+dis[v](到根的距离)
    • ②在一个子树中,那么就找到该子树的根,然后就类似于第一种情况
    • 其实就是分治的思想,把树不断分解成子树然后求解
    • 点分治过程中,每一层的所有递归过程合计对每个点处理一次, 假设共递归T层,则总时间复杂度为O(TNlogN)
    • 然而,如果树退化成一条链, 那么递归层数就是T=n,总时间复杂度为O(N^2logN)
    • 这样的复杂度接受不了,那么就要减少树的层数,那么每次就要找到树的中心来作为根,这样才能减少层数
    • 然后就是点分治了

    代码

     1 #include <cstdio> 
     2 #include <iostream>
     3 #include <queue>
     4 #include <vector>
     5 using namespace std;
     6 const int inf=10000000;
     7 const int N=100010;
     8 struct edge{int to,from,v;}e[N*2];
     9 int n,m,num,root,ans,cnt,head[N],mx[N],size[N],dis[N],dfn[N],vis[N],g[inf],w[inf],p[N],k[1010];
    10 void insert(int x,int y,int z) { e[++cnt].to=y; e[cnt].from=head[x]; e[cnt].v=z; head[x]=cnt; }
    11 void getroot(int x,int fa)
    12 {
    13     size[x]=1,mx[x]=0;
    14     for (int i=head[x];i;i=e[i].from)
    15         if (e[i].to!=fa&&!vis[e[i].to])
    16         {
    17             getroot(e[i].to,x);
    18             size[x]+=size[e[i].to];
    19             mx[x]=max(mx[x],size[e[i].to]);
    20         }
    21     mx[x]=max(mx[x],num-size[x]);
    22     if (mx[x]<mx[root]) root=x;
    23 }
    24 void getdis(int x,int fa)
    25 {
    26     dfn[++dfn[0]]=dis[x];
    27     for (int i=head[x];i;i=e[i].from) 
    28         if (e[i].to!=fa&&!vis[e[i].to])
    29             dis[e[i].to]=dis[x]+e[i].v,getdis(e[i].to,x);
    30 }
    31 void work(int x)
    32 {
    33     p[0]=0;
    34     for (int i=head[x];i;i=e[i].from)
    35         if (!vis[e[i].to])
    36         {
    37             dis[e[i].to]=e[i].v,dfn[0]=0;
    38             getdis(e[i].to,x);
    39             for (int j=dfn[0];j;j--)
    40                 for (int q=1;q<=m;q++)
    41                     if (k[q]>=dfn[j]) g[q]|=w[k[q]-dfn[j]];
    42             for (int j=dfn[0];j;j--) p[++p[0]]=dfn[j],w[dfn[j]]=1;
    43         }
    44     for (int i=1;i<=p[0];i++) w[p[i]]=0;
    45 }
    46 void cdq(int x)
    47 {
    48     vis[x]=w[0]=1,work(x);
    49     for (int i=head[x];i;i=e[i].from)
    50         if (!vis[e[i].to])
    51             num=size[e[i].to],mx[root=0]=inf,getroot(e[i].to,0),cdq(root);
    52 }
    53 int main() 
    54 {
    55     scanf("%d%d",&n,&m);
    56     for (int i=1,x,y,z;i<n;i++) scanf("%d%d%d",&x,&y,&z),insert(x,y,z),insert(y,x,z);
    57     for (int i=1;i<=m;i++) scanf("%d",&k[i]);
    58     mx[root]=num=n,getroot(1,0),cdq(root);
    59     for (int i=1;i<=m;i++) if (g[i]) printf("AYE
    "); else printf("NAY
    ");
    60 }
  • 相关阅读:
    maven settings.xml 文件
    Ajax配合后端实现Excel的导出
    线性表的链式存储--单链表
    微软最新开源工具PowerToys,让你的win10更加好用!!
    二叉树合集(三):线索二叉树(图文详解)
    二叉树合集(二):霍夫曼树(图文详解)
    二叉树合集(一):二叉树基础(含四种遍历,图文详解)
    二叉树合集(六):高度平衡的二叉搜索树简介(图文解析)
    二叉树合集(五):二叉搜索树(图片详解,含基本操作)
    【LeetCode】108.将有序数组转换为平衡二叉树(中序遍历三种方法,java实现)
  • 原文地址:https://www.cnblogs.com/Comfortable/p/9813348.html
Copyright © 2020-2023  润新知