题目链接:http://poj.org/problem?id=1741

Tree
Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 35091   Accepted: 11718

Description

Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.

Input

The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.

Output

For each test case output the answer on a single line.

Sample Input

5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

Sample Output

8

Source

题意:一棵有n个节点的树,每条边有个权值代表相邻2个点的距离,要求求出所有距离不超过k的点对(u,v)

题解:树分治
假设树以root为根节点,那么满足要求的点对有2种情况:
①路径经过root且dis(u,v)<=k
②路径不经过root,即其路径的最高点为子树上某一节点

对于第②种情况可以通过递归求解,这里只讨论第一种情况
该如何求解路径经过root且dis(u,v)<=k的合法点对数呢?

设dir[u]为u到根节点root的距离,那么只有满足dir[u]+dir[v]<=k且LCA(u,v)==root的点对才是合法的,
设cnt1=树中所有dis(u,v)<=k的点对数,cnt2=LCA(u,v)==root的子节点的合法点对数
那么以root为根的树种合法点对数为:ans=cnt1-cnt2
找出有多少个dir[u]+dir[v]的方法很简单:只需要排序后扫一遍即可。

总结一下算法的过程:
①计算以u为根的树种每棵子树的大小
②根据子树大小找出树的重心root(以树的重心为根的树,可以使其根的子树中节点最多的子树的节点最少)
③以root为根,计算树中每个点到root的距离dir
④计算树中所有满足dir[u]+dir[v]<=k的点对数cnt1
⑤计算以root的子节点为根的子树中,满足dir[u]+dir[v]<=k的点对数cnt2
⑥ans+=cnt1-cnt2
注意:每次计算完cnt1后,要将vis[root]=1,这样就可以将一棵树分解成若干棵子树

看代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn=2e4+5;
const int INF=1e9+7;
int cnt=0;
int head[maxn<<1];
int sonnum[maxn<<1],sonmax[maxn<<1];
int mi,pos;
int N,K;
int sum=0;
bool vis[maxn<<1];
LL ans;
vector<int>dis;
struct Edge
{int next,to,w;
}e[maxn<<1];
void Init()
{ans=0;cnt=0;for(int i=0;i<maxn;i++){head[i]=-1;sonnum[i]=sonmax[i]=0;vis[i]=false;}
}
void add_edge(int u,int v,int w)
{e[++cnt].to=v;e[cnt].w=w;e[cnt].next=head[u];head[u]=cnt;
}
void Query_size(int root,int pre)//求当前树的子树大小
{sum++;//存树的大小sonnum[root]=1;sonmax[root]=0;//注意初始化 for(int i=head[root];i!=-1;i=e[i].next){int v=e[i].to;if(vis[v]||v==pre) continue;Query_size(v,root);sonnum[root]+=sonnum[v];//子树节点有多少个sonmax[root]=max(sonmax[root],sonnum[v]);//最大的子树节点个数
    }
}
void Query_root(int root,int pre,int sum)//求当前树的重心
{for(int i=head[root];i!=-1;i=e[i].next){int v=e[i].to;if(vis[v]||v==pre) continue;Query_root(v,root,sum);}int ma=max(sonmax[root],sum-sonnum[root]);if(mi>ma){mi=ma;pos=root;}
}
void Query_dis(int root,int pre,int d)
{dis.push_back(d);for(int i=head[root];i!=-1;i=e[i].next){int v=e[i].to,w=e[i].w;if(vis[v]||v==pre) continue;Query_dis(v,root,d+w);}
}
int cal(int root,int d)
{int ret=0;dis.clear();//存所有子节点到本身的距离Query_dis(root,0,d);sort(dis.begin(),dis.end());int i=0,j=dis.size()-1;while(i<j){while(i<j&&dis[i]+dis[j]>K) j--;ret+=j-i;i++;}return ret;
}
void dfs(int root,int pre)
{sum=0;mi=INF;Query_size(root,pre);Query_root(root,pre,sum);//
    int rt=pos;ans+=cal(rt,0);//pos为找到的重心vis[rt]=true;//一定要标记 否则会往回走for(int i=head[rt];i!=-1;i=e[i].next){int v=e[i].to,w=e[i].w;if(vis[v]) continue;ans-=cal(v,w);dfs(v,rt);}
}
int main()
{while(scanf("%d%d",&N,&K)!=EOF){Init();if(N==0&&K==0) break;for(int i=1;i<N;i++){int u,v,w;scanf("%d%d%d",&u,&v,&w);add_edge(u,v,w);add_edge(v,u,w);}dfs(1,0);printf("%lld\n",ans);}return 0;
}

转载于:https://www.cnblogs.com/caijiaming/p/11569226.html

Tree(树分治入门)相关推荐

  1. POJ1741 Tree 树中点对统计【树分治入门】

    算法合集之<分治算法在树的路径问题中的应用> 论文下载地址 树被定义为没有圈的连通图,有几个性质 在树中去掉一条边后,得到的图是不连通的 在树中添加一条边后,一定存在一条边 树的每一对顶点 ...

  2. POJ 1741 Tree 树分治

    题意: 给出一颗有\(n (n \leq 10^4)\)个节点的树,和一个\(k\).统计有多少个点对\(u, \, v(u \neq v)\)满足\(u\)到\(v\)的最短距离不超过\(k\). ...

  3. POJ 1741 Tree(树分治)

    去网上搜题解大多数都是说论文,搜了论文看了看,写的确实挺好,直接复制过来. 不过代码中有些细节还是要注意的,参考这篇http://blog.sina.com.cn/s/blog_6d5aa19a010 ...

  4. POJ1741 Tree(树分治——点分治)题解

    题意:给一棵树,问你最多能找到几个组合(u,v),使得两点距离不超过k. 思路:点分治,复杂度O(nlogn*logn).看了半天还是有点模糊. 显然,所有满足要求的组合,连接这两个点,他们必然经过他 ...

  5. POJ1741:Tree——题解+树分治简要讲解

    http://poj.org/problem?id=1741 题目大意:给一棵树,求点对间距离<=k的个数. -------------------- 以这道题为例记录一下对于树分治的理解. 树 ...

  6. 【楼天城男人八题】【树分治|Treap+启发式合并】POJ1741 Tree

    题面在这里 待我先膜拜一下楼教主-- 首先这题是很明显的树分治 想说点什么却发现已经没什么好说了 然后我们来看另一种解法:平衡树乱搞 这里用的是Treap实现 对于每个节点,用Treap记录该子树每个 ...

  7. 点分治(树分治)详解

    作者: hsez_yyh 链接:https://blog.csdn.net/yyh_getAC/article/details/126696654 来源:湖北省黄石二中信息竞赛组        著作权 ...

  8. CodeForces - 1217F Forced Online Queries Problem(线段树分治+并查集撤销)

    题目链接:点击查看 题目大意:给出 nnn 个点,初始时互相不存在连边,需要执行 mmm 次操作,每次操作分为两种类型: 1xy1 \ x \ y1 x y:如果 (x,y)(x,y)(x,y) 之间 ...

  9. 牛客多校8 - All-Star Game(线段树分治+并查集按秩合并的撤销操作)

    题目链接:点击查看 题目大意:有 n 个球员和 m 个球迷,一个球员可能是多个球迷的粉丝,需要选择最少的球员进行比赛,使得所有的球迷都愿意观看(对于每个球迷来说,都有至少一个其喜欢的球员入选比赛) 对 ...

最新文章

  1. iOS Automated Tests with UIAutomation
  2. Keras之DNN:利用DNN算法【Input(8)→12+8(relu)→O(sigmoid)】利用糖尿病数据集训练、评估模型(利用糖尿病数据集中的八个参数特征预测一个0或1结果)
  3. 有关打印、收藏等的JS代码(打印等主要使用了一个IE组件来实现)
  4. Maven resource artifact download url population logic naming convention
  5. [P1580] yyy loves Easter_Egg I
  6. QT跨平台项目开发经验(项目打包)
  7. mongodb 存储过程 遍历表数据_mongodb推荐存列表字段还是多条记录?
  8. 浅谈c#中使用lock的是与非
  9. 浙江财经大学是一所怎样的学校?
  10. 【WIP_S3】链表
  11. Kotlin实战【四】迭代事物:while和for
  12. JavaScript常识 js代码位置 调用外部js文件
  13. 小学生组词词典 官方
  14. CATIA V5 R24 2014安装教程
  15. 汇编语言自定义int9中断程序
  16. BT源代码学习心得(十五):客户端源代码分析(下载过程中的块选取策略)
  17. java.lang.NullPointerException: null无堆栈信息
  18. IA32 gnu assembly 32 bit instruction pretend to be 64 bit instruction
  19. 信号完整性的定义、干扰因素及解决方法详解
  20. 使用计算机对炼钢过程进行实时监控,新钢炼钢-轧钢生产过程缓冲环节的解析、优化与控制 张志宏...

热门文章

  1. C++ 出现异常“.... \debug_heap.cpp Line:980 Expression:__acrt_first_block==header“
  2. window server 2008配置FTP服务器550 Access is denied. 问题解决办法
  3. pytorch一天速成第一部分——基础入门Tensor和cuda
  4. 算法大赛十强战队解题方案大公开!【附PPT下载】
  5. 谁与争锋,2020腾讯广告算法大赛初赛正式启动
  6. png文件合并_程序员学习之在Python中使用PDF:阅读、旋转、合并和拆分
  7. 单片机原理及应用pdf_单片机原理及应用课程设计
  8. 关于TikTok的变现思考和三种玩法
  9. ajax ashx 请选择文件,ajax+jquery+ashx如何实现上传文件
  10. CCCC/PTA 2019模拟赛 L3-3 至多删三个字符