题意:给一棵树,n个节点,给定一个数k,求任意满足dist(a,b)<=k的点对的数量。

思路:

  这道题的思路比较简单,但是细节很多。

  此题可以用分治法,如何分治?

  (1)如果path(a,b)不经过根,那么肯定在根的某棵子树下,递归解决。

  (2)如果path(a,b)经过根,那么肯定在根的不同子树下,处理它。

  怎样处理?如果知道了每棵子树中的节点到根的距离,暂且将每棵子树的节点分到每个独立的桶里。每个节点都可以和其他桶里的点组成点对,只要距离<=k的话就满足要求了。逐个算可能超时了,用个简单点的方法。在这里我们不需要知道节点是谁,只需要知道距离,所以将所有节点到根的距离取出来,排个序,用两个指针在线性复杂度就可以解决本节点下的点对统计了。

  但是如何去重?因为所有子树中的节点全部都混着排序了,估计会有挺多对是在同棵子树下的节点被统计到了,这暂时不需要,这是要递归解决的,那么就要去掉这些同棵树下的点对。计算的方法都是一样的。

  但是还有一种情况让你TLE,就是单链时的情况,若每次以孩子来递归下去解决,可能就不行了。但是可以发现到,计算这棵子树时,完全也不用经过根,那么不妨直接找出这棵子树中的重心作为根来解决这个子问题。复杂度主要是在排序的地方,用了找重心的方法可以保证每个点最多被排序2*logn次,所以总复杂度为O(nlog2n)。

  如下图,红色点为重心,红色的线为虚拟边。左图是原树,右图是分治的过程。就像是每个重心隔开了一些子树一样。

  

  有论文可以看。

 //#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int N=;
int n, k, root, edge_cnt, ans, num, big, vis[N];
vector<int> vect[N];
struct node
{
int from,to,len;
node(){};
node(int from,int to,int len):from(from),to(to),len (len){};
}edge[N*]; void add_edge(int from,int to,int len)
{
edge[edge_cnt]=node(from,to,len);
vect[from].push_back(edge_cnt++);
} int get_size(int t,int far)
{
int cnt=;
for(int i=; i<vect[t].size(); i++)
{
node e=edge[vect[t][i]];
if(e.to!=far&&!vis[e.to]) cnt+=get_size(e.to, t);
}
return cnt;
} int get_root(int t,int far) //求重心:root。
{
int cur=, sum=;
for(int i=; i<vect[t].size(); i++)
{
node e=edge[vect[t][i]];
if( e.to!=far && !vis[e.to] )
{
int tmp=get_root(e.to, t);
sum+=tmp;
cur=max(cur, tmp);
}
}
cur=max(cur, num-sum);
if(cur<big) big=cur, root=t; //更新root
return sum;
} vector<int> seq;
void get_all(int t,int far,int len) //获取子树t的所有节点
{
seq.push_back(len);
if(len>=k) return ; //剪枝
for(int i=; i<vect[t].size(); i++)
{
node e=edge[vect[t][i]];
if( !vis[e.to] && e.to!=far ) get_all(e.to, t, len+e.len);
}
} int cal(int t,int len) //计算子树t的内部点对。
{
seq.clear();
get_all(t, , len); //将以t为根的整棵子树的len装进去seq
sort(seq.begin(), seq.end());
int tmp=, L=, R=seq.size()-;
while( L<R )
{
if( seq[L]+seq[R]<=k ) tmp+=R-L,L++;
else R--;
}
return tmp;
} void solve(int t) //主函数
{
vis[t]=;
ans+=cal(t, ); //计算子树t的答案
for(int i=; i<vect[t].size(); i++)
{
node e=edge[vect[t][i]];
if( !vis[e.to] )
{
ans-=cal(e.to, e.len); //去重
big=INF;root=-;num=get_size(e.to, -); //用于找重心
get_root(e.to, -);
solve(root);
}
}
} void init()
{
for(int i=; i<=n; i++)vect[i].clear();
memset(vis, , sizeof(vis));
ans=edge_cnt=;
} int main()
{
//freopen("input.txt", "r", stdin);
int a,b,c;
while( scanf("%d%d",&n,&k), n+k )
{
init();
for(int i=; i<n; i++)
{
scanf("%d%d%d",&a,&b,&c);
add_edge(a,b,c);
add_edge(b,a,c);
}
num=n;big=INF;root=-; //找重心用的。
get_root(,-); //找重心
solve( root );
printf("%d\n", ans);
}
return ;
}

AC代码

POJ 1741 Tree (树的分治,树的重心)的更多相关文章

  1. POJ 1741 Tree 树上点分治

    题目链接:http://poj.org/problem?id=1741 题意: 给定一棵包含$n$个点的带边权树,求距离小于等于K的点对数量 题解: 显然,枚举所有点的子树可以获得答案,但是朴素发$O ...

  2. POJ 1741 Tree (点分治)

                                                                        Tree Time Limit: 1000MS   Memory ...

  3. poj 1741 Tree(点分治)

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 15548   Accepted: 5054 Description ...

  4. poj 1741 Tree(树的点分治)

    poj 1741 Tree(树的点分治) 给出一个n个结点的树和一个整数k,问有多少个距离不超过k的点对. 首先对于一个树中的点对,要么经过根结点,要么不经过.所以我们可以把经过根节点的符合点对统计出 ...

  5. POJ 1741.Tree and 洛谷 P4178 Tree-树分治(点分治,容斥版) +二分 模板题-区间点对最短距离<=K的点对数量

    POJ 1741. Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 34141   Accepted: 11420 ...

  6. POJ 1741 Tree 求树上路径小于k的点对个数)

                                                                                                 POJ 174 ...

  7. POJ 1741 Tree (树分治入门)

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 8554   Accepted: 2545 Description ...

  8. POJ 1741 Tree 树的分治

    原题链接:http://poj.org/problem?id=1741 题意: 给你棵树,询问有多少点对,使得这条路径上的权值和小于K 题解: 就..大约就是树的分治 代码: #include< ...

  9. POJ 1741 Tree 树形DP(分治)

    链接:id=1741">http://poj.org/problem?id=1741 题意:给出一棵树,节点数为N(N<=10000),给出N-1条边的两点和权值,给出数值k,问 ...

  10. POJ 1741 Tree(点分治点对<=k)

    Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Def ...

随机推荐

  1. 通过上一节部署出来的 Windows instance 有时候会发现操作系统时间总是慢 8 个小时,即使手工调整好时间和时区,下次 instance 重启后又会差 8 个小时

    这是 OpenStack 实施经验分享系列的第 3 篇. 问题描述 通过上一节部署出来的 Windows instance 有时候会发现操作系统时间总是慢 8 个小时,即使手工调整好时间和时区,下次 ...

  2. IReport制作报表——日期时间显示格式

    转自:https://blog.csdn.net/linglinglu/article/details/9022679?utm_source=blogxgwz2 IReport工具在制作报表的时候,会 ...

  3. 关于git被误删除的分支还原问题

    在开发过程中, 有可能会将正在开发的本地分支误删, 本地分支被删除时, 如果已经将本地分支的变更推送到了远端, 还没有问题, 如果被删除的本地分支只提交了没有推送到远端, 就悲剧了, 相当于在你上一次 ...

  4. Sherlock and the Encrypted Data

    题意: 对于16进制数字num,假定 $p_0,p_1,...,p_m$ 在该数字中出现过,如果有 $x = 2^{p_0} + 2^{p_1} + ... + 2^{p_m}$ 且 $x \oplu ...

  5. 洛谷 - P2424 - 约数和 - 整除分块

    https://www.luogu.org/problemnew/show/P2424 记 \(\sigma(n)\) 为n的所有约数之和,例如 \(\sigma(6)=1+2+3+6=12\) . ...

  6. HDU4973 【几何。】

    题意: 给你一个以原点为圆心的两个圆,一个大圆,一个小圆,然后给你一个硬币和他的速度,问你经过大圆的时间: 思路: 直接杠.. 然后wa的怀疑人生,后面wa在了速度的方向,如果我说一个点在两个圆的左上 ...

  7. Unity中资源动态加载的几种方式比较

    http://blog.csdn.net/leonwei/article/details/18406103 初学Unity的过程中,会发现打包发布程序后,unity会自动将场景需要引用到的资源打包到安 ...

  8. C/C++函数调用过程分析

    http://www.cnblogs.com/biyeymyhjob/archive/2012/07/20/2601204.html 这里以一个简单的C语言代码为例,来分析函数调用过程 代码: #in ...

  9. web前端篇:JavaScript基础篇(易懂小白上手快)-1

    目录 详细内容: 0.JavaScript的引入 1.第一个JavaScript 2.变量 3.变量的类型 4.数组 5.条件语句 6.三元运算符 7.循环 8.函数 9.对象(object): 10 ...

  10. 集成Activiti工作流的J2EE快速开发框架

    框架简介 enos款快速开发模块化脚手架,实现功能有系统模块:菜单管理.用户管理.角色管理,系统监控:系统日志.接口api.sql监控. 系统功能 系统管理:菜单管理.用户管理.角色管理 统一查询 p ...