【洛谷P3806 点分治】

题目描述

  给定一棵有n个点的树

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

输入格式:

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

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

输出格式:

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

输入样例:

 2 1
 1 2 2
 2

输出样例:

 AYE

题解:

  离线处理,先对这棵树进行点分治,可以预处理出来每种长度的边有没有出现过,然后再判断即可。

  对于每个子问题,由重心出发dfs,把所有深度两两相加,打标记(根深度为0),然后有两个点都属于同一个子树,那么这两个节点算出来的答案是不存在的,再以重心的儿子开始dfs(深度与第一次dfs相同)把其中所有深度两两删掉即可。

  

 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 using namespace std;
 7 struct nd{
 8     int to,next,v;
 9 }    e[20005];
10 int head[10005],dep[10005],d[10005],mx[10005],sz[10005];
11 bool vis[10005];
12 int t[10000005];
13 int sum,ans,rt;
14 int n,k,cnt,m;
15 inline void insert(int u,int v,int w){
16     e[++cnt].next=head[u];
17     head[u]=cnt;
18     e[cnt].to=v;
19     e[cnt].v=w;
20 }
21 inline void findG(int now,int fa){
22     mx[now]=0;
23     for(int i=head[now];i;i=e[i].next){
24         if(e[i].to==fa || vis[e[i].to])    continue;
25         findG(e[i].to,now);
26         mx[now]=max(mx[now],sz[e[i].to]);
27     }
28     mx[now]=max(mx[now],sum-sz[now]);
29     if(mx[now]<mx[rt])    rt=now;
30 }
31 inline void findS(int now,int fa){
32     sz[now]=1;
33     for(int i=head[now];i;i=e[i].next){
34         if(vis[e[i].to] || e[i].to==fa)    continue;
35         findS(e[i].to,now);
36         sz[now]+=sz[e[i].to];
37     }
38 }
39 inline void findD(int now,int fa){
40     dep[++dep[0]]=d[now];
41     for(int i=head[now];i;i=e[i].next){
42         if(vis[e[i].to] || e[i].to==fa)    continue;
43         d[e[i].to]=d[now]+e[i].v;
44         findD(e[i].to,now);
45     }
46 }
47 inline int cal(int now,int x,int k){
48     d[now]=x;dep[0]=0;
49     findD(now,-1);
50     int num=0,l,r;
51     for(int i=1;i<=dep[0];i++)
52         for(int j=i+1;j<=dep[0];j++)
53             t[dep[i]+dep[j]]+=k;
54 }
55 inline void work(int x)
56 {
57     cal(x,0,1);
58     vis[x]=1;
59     for(int i=head[x];i;i=e[i].next){
60         if(vis[e[i].to])    continue;
61         cal(e[i].to,e[i].v,-1);
62         findS(e[i].to,x);
63         sum=sz[e[i].to];
64         rt=0;
65         findG(e[i].to,x);
66         work(rt);
67     }
68 }
69 int main(){
70     int u,v,w,x;
71     scanf("%d%d",&n,&m);
72     for(int i=1;i<n;i++){
73         scanf("%d%d%d",&u,&v,&w);
74         insert(u,v,w);
75         insert(v,u,w);
76     }
77     rt=0;
78     mx[rt]=1e9+7;
79     findS(1,-1);
80     sum=sz[1];
81     findG(1,-1);
82     work(rt);
83     for(int i=1;i<=m;i++){
84         scanf("%d",&x);
85         if(t[x]>=1)    printf("AYE\n");
86         else    printf("NAY\n");
87     }
88 }

 

转载于:https://www.cnblogs.com/Dndlns/p/7906566.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值