题目大意:

给一棵有 n 个顶点的树,每条边都有一个长度(小于 1001 的正整数)。
定义 dist(u,v)=节点 u 和 v 之间的最小距离。
给定一个整数 k,对于每一对 (u,v) 顶点当且仅当 dist(u,v) 不超过 k 时才称为有效。
编写一个程序,计算给定树有多少对有效。

算法分析:

这道题是一道标准的点分治模板题。题目给定的树是一颗无根树,我们首先选取点作为根节点,这里选取树的重心root作为划分点,使得将树划分得尽量均衡。然后我们统计每个点到根节点的距离,将这些距离加入到一个距离数组中,排序,用双指针扫描,就可以统计以root为根的满足条件的节点数,这里还要用到容斥的思想,我们对root的子树v都要减去重复统计的节点数,从v出发重复以上的过程。

要算两个节点之间的最小距离不超过k,以root为根,则有两种情况:

1.两个点在以root为根的同一颗子树中;

2.两个点在不同子树中;

  1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 const int maxn=10005;
6 int cnt,n,k,ans,head[maxn];
7 int root,S,size[maxn],f[maxn],d[maxn],dep[maxn];
8 bool vis[maxn];
9 struct edge
10 {
11 int to,next,w;
12 }edge[maxn*2];
13
14 void add(int u,int v,int w)
15 {
16 edge[++cnt].to=v;
17 edge[cnt].w=w;
18 edge[cnt].next=head[u];
19 head[u]=cnt;
20 }
21
22 void getroot(int u,int fa)//获取重心
23 {
24 size[u]=1;
25 f[u]=0;//删除u后,最大子树的大小
26 for(int i=head[u];i;i=edge[i].next)
27 {
28 int v=edge[i].to;
29 if(v!=fa&&!vis[v])
30 {
31 getroot(v,u);
32 size[u]+=size[v];
33 f[u]=max(f[u],size[v]);
34 }
35 }
36 f[u]=max(f[u],S-size[u]);//S为当前子树总结点数
37 if(f[u]<f[root])
38 root=u;
39 }
40
41 void getdep(int u,int fa)//获取距离
42 {
43 dep[++dep[0]]=d[u];//保存距离数组
44 for(int i=head[u];i;i=edge[i].next)
45 {
46 int v=edge[i].to;
47 if(v!=fa&&!vis[v])
48 {
49 d[v]=d[u]+edge[i].w;
50 getdep(v,u);
51 }
52 }
53 }
54
55 int getsum(int u,int dis) //获取u的子树中满足个数
56 {
57 d[u]=dis;
58 dep[0]=0;
59 getdep(u,0);
60 sort(dep+1,dep+1+dep[0]);
61 int L=1,R=dep[0],sum=0;
62 while(L<R)
63 if(dep[L]+dep[R]<=k)
64 sum+=R-L,L++;
65 else
66 R--;
67 return sum;
68 }
69
70 void solve(int u) //获取答案
71 {
72 vis[u]=true;
73 ans+=getsum(u,0);
74 for(int i=head[u];i;i=edge[i].next)
75 {
76 int v=edge[i].to;
77 if(!vis[v])
78 {
79 ans-=getsum(v,edge[i].w);//减去重复
80 root=0;
81 S=size[v];
82 getroot(v,u);
83 solve(root);
84 }
85 }
86 }
87
88 int main()
89 {
90 f[0]=0x7fffffff;//初始化树根
91 while(scanf("%d%d",&n,&k),n+k)
92 {
93 memset(vis,0,sizeof(vis));
94 memset(head,0,sizeof(head));
95 cnt=0;ans=0;
96 for(int i=1;i<=n-1;i++)
97 {
98 int x,y,z;
99 scanf("%d%d%d",&x,&y,&z);
100 add(x,y,z);
101 add(y,x,z);
102 }
103 root=0;
104 S=n;
105 getroot(1,0);
106 solve(root);
107 printf("%d\n",ans);
108 }
109 return 0;
110 }

每次选取重心作为划分点,点分治最多递归O(logn)层,距离数组排序O(nlogn),总的时间复杂度O(n)。

POJ1741 tree (点分治模板)的更多相关文章

  1. POJ1741 Tree 树分治模板

    http://poj.org/problem?id=1741   题意:一棵n个点的树,每条边有距离v,求该树中距离小于等于k的点的对数.   dis[y]表示点y到根x的距离,v代表根到子树根的距离 ...

  2. [poj1741]Tree(点分治+容斥原理)

    题意:求树中点对距离<=k的无序点对个数. 解题关键:树上点分治,这个分治并没有传统分治的合并过程,只是分成各个小问题,并将各个小问题的答案相加即可,也就是每层的复杂度并不在合并的过程,是在每层 ...

  3. POJ - 1741 - Tree - 点分治 模板

    POJ-1741 题意: 对于带权的一棵树,求树中距离不超过k的点的对数. 思路: 点分治的裸题. 将这棵树分成很多小的树,分治求解. #include <algorithm> #incl ...

  4. [POJ1741]Tree(点分治)

    树分治之点分治入门 所谓点分治,就是对于树针对点的分治处理 首先找出重心以保证时间复杂度 然后递归处理所有子树 对于这道题,对于点对(u,v)满足dis(u,v)<=k,分2种情况 路径过当前根 ...

  5. [bzoj1468][poj1741]Tree[点分治]

    可以说是点分治第一题,之前那道的点分治只是模模糊糊,做完这道题感觉清楚了很多,点分治可以理解为每次树的重心(这样会把数分为若干棵子树,子树大小为log级别),然后统计包含重心的整个子树的值减去各个子树 ...

  6. bzoj 1468 Tree(点分治模板)

    1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1527  Solved: 818[Submit][Status][Discuss] ...

  7. [洛谷P4178] Tree (点分治模板)

    题目略了吧,就是一棵树上有多少个点对之间的距离 \(\leq k\) \(n \leq 40000\) 算法 首先有一个 \(O(n^2)\) 的做法,枚举每一个点为起点,\(dfs\) 一遍可知其它 ...

  8. POJ1741 Tree + BZOJ1468 Tree 【点分治】

    POJ1741 Tree + BZOJ1468 Tree Description Give a tree with n vertices,each edge has a length(positive ...

  9. poj1741 Tree(点分治)

    题目链接:http://poj.org/problem?id=1741 题意:求树上两点之间距离小于等于k的点对的数量 思路:点分治模板题,推荐一篇讲的非常好的博客:https://blog.csdn ...

随机推荐

  1. CF576A Vasya and Petya's Game

    题目大意: 给定一个数字 n,现在 Vasya 要从 1∼n 中想一个数字 x. Petya 向 Vasya 询问 "x 是否能整除 y?" ,通过 Vasya 的回答来判断 x ...

  2. C# 实例解释面向对象编程中的依赖反转原则

    在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...

  3. 使用Pure Pipes来替换HTML里面的纯函数

    <ul *ngFor="let item of list"> <li>{{show(item.label)}}</li> </ul> ...

  4. LevelSequence源码分析

    前言 这篇文章主要讲的是Unreal LevelSequence RunTime的部分.即在游戏中运行Level Sequence的源码解析.(而且抛去Replicated 的Sequence,一般S ...

  5. 从零开始Blazor Server(5)--权限验证

    序 之前我们一直使用的是微软自带的身份验证方式,即使用[Authorize]标签来做. 但是这种方式十分不灵活,微软推荐的方式是加Policy,但是这种方式对我们来说还是不够灵活. 所以本节我们用完全 ...

  6. Ubuntu14.04或16.04下普通用户的root权限获得

    Ubuntu系统默认不允许使用root登录,因此初始root帐户是不能使用的,需要在普通账户下利用sudo权限修改root密码.然后以root帐户进行相关操作. 具体操作: 1.打开系统,用普通帐户登 ...

  7. 从 Delta 2.0 开始聊聊我们需要怎样的数据湖

    盘点行业内近期发生的大事,Delta 2.0 的开源是最让人津津乐道的,尤其在 Databricks 官宣 delta2.0 时抛出了下面这张性能对比,颇有些引战的味道. 虽然 Databricks ...

  8. Luogu3275 [SCOI2011]糖果 (差分约束)

    逆序建超级源快十倍还行 #include <cstdio> #include <iostream> #include <cstring> #include < ...

  9. LuoguP1922 女仆咖啡厅桌游吧 (树形动态规划)

    #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...

  10. java-数组排序之冒泡排序(经典排序)

    public class BubbleSort { public static void main(String[] args) { /*冒泡排序不一定是用时最短的 * 1)声明整型数组arr,包含1 ...