POJ 1741 Tree 求树上路径小于k的点对个数)
Description
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 last test case is followed by two zeros.
Output
Sample Input
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
Sample Output
8
题目大意:有一颗由n个点组成的树,问树上两点间距离小于等于k的点对有多少对
输入:多组数据输入。每组数据第1行n,k,接下来n-1行,u,v,l表示点u与点v之间有一条长为l的边
输出:点对个数
基本算法:点分治
点分治,本质还是分治算法
对于一棵树,简单的递归搜索的复杂度,呵呵~~,
所以为了降低复杂度,通俗点儿说就是将一棵树拆开
一棵树的复杂度之所以高,是因为它有可能很深,
所以拆要使拆开后的几棵树最深的最小
那么选取的这个点就是树的重心
树的重心通俗点儿说就是删除重心后最大的连通块最小
找出重心后,树上的点的路径就可以分为
经过重心的 和 不过重心的
对于经过重心的,
1、统计出过重心的所有点的满足条件的数目=ans1
2、对于每棵子树,统计一遍自己内部满足条件的数目=ans2
ans=ans1-所有的ans2
对于不经过重心的,继续递归
本人点分治理解不深,对点分治更详细的解读 推荐博客:http://www.cnblogs.com/chty/p/5912360.html对于文章中出现的错误,欢迎各位指正
代码中数组含义:head[],链表 son[i]=j,以i为根的所有子树总共有j个节点(包括i)
f[i]=j以i为根的所有子树中,最大的一颗子树有j个节点(不包括i)
sum,当前计算的树或子树的点的个数
d[i]=j,点i到当前所选的根节点距离为j deep[],d数组的汇总
代码中函数作用:getroot,找重心 getdeep,统计点之间的距离 cal,统计满足条件的点对数目
部分代码细节:
getroot函数:son[x]=1,因为son包含自己 f[x]=0,因为f可能存有上一次的结果
f[x]=max(f[x],sum-son[x]);①解释了为什么son包含自己,sum是总点数,son[x]是除临时指定的父节点所在子树的子树节点总数,相减就是临时父节点所在子树节点总数
因为父节点是临时指定的,所以也有可能成为x的孩子节点,所以父节点所在子树也作为x的一颗子树 ②在>2个点时,保证不让叶子节点成为重心
work函数:root=0 && main函数 f[0]=inf 这两个互相照应,删除选定的根之后,让根=0,因为f[0]=inf,这样在getroot函数里才保证了f[x]<f[root],更新root
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 10010
#define inf 20001
using namespace std;
int n,k,cnt,head[N],son[N],f[N],sum,ans,root,d[N],deep[N];
bool vis[N];
struct node
{
int next,to,w;
}e[*N];
inline void add(int u,int v,int w)
{
e[++cnt].to=v;e[cnt].w=w;e[cnt].next=head[u];head[u]=cnt;
e[++cnt].to=u;e[cnt].w=w;e[cnt].next=head[v];head[v]=cnt;
}
inline void pre()
{
memset(head,,sizeof(head));
memset(vis,false,sizeof(vis));
ans=;cnt=;root=;
}
inline void getroot(int x,int fa)
{
son[x]=;f[x]=;
for(int i=head[x];i;i=e[i].next)
{
if(e[i].to==fa||vis[e[i].to]) continue;
getroot(e[i].to,x);
son[x]+=son[e[i].to];
f[x]=max(f[x],son[e[i].to]);
}
f[x]=max(f[x],sum-son[x]);
if(f[x]<f[root]) root=x;
}
inline void getdeep(int x,int fa)
{
deep[++deep[]]=d[x];
for(int i=head[x];i;i=e[i].next)
{
if(e[i].to==fa||vis[e[i].to]) continue;
d[e[i].to]=d[x]+e[i].w;
getdeep(e[i].to,x);
}
}
inline int cal(int x,int p)
{
d[x]=p;deep[]=;
getdeep(x,);
sort(deep+,deep+deep[]+);
int t=,l,r;
for(l=,r=deep[];l<r;)
{
if(deep[l]+deep[r]<=k) {t+=r-l;l++;}
else r--;
}
return t;
}
inline void work(int x)
{
ans+=cal(x,);
vis[x]=true;
for(int i=head[x];i;i=e[i].next)
{
if(vis[e[i].to]) continue;
ans-=cal(e[i].to,e[i].w);
sum=son[e[i].to];
root=;
getroot(e[i].to,);
work(root);
}
}
int main()
{
while()
{
scanf("%d%d",&n,&k);
if(!n) return ;
pre();
int u,v,w;
for(int i=;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
sum=n;f[]=inf;
getroot(,);
work(root);
printf("%d\n",ans);
}
}
加的是无向边,链表忘了开双倍,RE。。。。。。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x7fffffff
using namespace std;
int n,K,cnt,sum,ans,root;
int head[],deep[],d[],f[],son[];
bool vis[];
struct data{int to,next,v;}e[];
inline int read()
{
int x=;char c=getchar();
while(c<''||c>'') c=getchar();
while(c>=''&&c<='') {x=x*+c-'';c=getchar();}
return x;
}
inline void insert(int u,int v,int w)
{
e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].v=w;
e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;e[cnt].v=w;
}
//最小递归层数 why联通的节点数量最小? 不是层数?
inline void getroot(int x,int fa)
{
son[x]=;f[x]=;//son:以x为根的子树的节点个数,包括自己
//f[x]=0 不能删 因为f[x]可能存有上一次的结果
for(int i=head[x];i;i=e[i].next)
{
if(e[i].to==fa||vis[e[i].to]) continue; //vis=true表示节点已删除
getroot(e[i].to,x);
son[x]+=son[e[i].to];
f[x]=max(f[x],son[e[i].to]);
}
f[x]=max(f[x],sum-son[x]);
//树本有根,点分治重新找根,所以以x为根的子树除了已递归到的,还有以父节点为根的子树,这也是son[x]=1的原因
if(f[x]<f[root]) root=x;//找到的根满足它的最大子树最小
}
inline void getdeep(int x,int fa)
{
deep[++deep[]]=d[x];//deep[0]总的节点数,deep 每个点到根节点的距离
for(int i=head[x];i;i=e[i].next)
{
if(e[i].to==fa||vis[e[i].to]) continue;
d[e[i].to]=d[x]+e[i].v;
getdeep(e[i].to,x);
}
}
inline int cal(int x,int now)//now初始为0
{
d[x]=now;deep[]=;//d是长度
getdeep(x,);//得到以x为根的子树中,每个点到x的距离
sort(deep+,deep+deep[]+);
int t=,l,r;
for(l=,r=deep[];l<r;)
{
if(deep[l]+deep[r]<=K) {t+=r-l;l++;}
else r--;
}
return t;
}
inline void work(int x)//x是确定的根
{
ans+=cal(x,);
vis[x]=;
for(int i=head[x];i;i=e[i].next)
{
if(vis[e[i].to]) continue;
ans-=cal(e[i].to,e[i].v);
sum=son[e[i].to];
root=;//删除原根节点后,重新找根节点,f【0】=inf
getroot(e[i].to,root);
work(root);
}
}
int main()
{
while()
{
ans=,root=,cnt=;
memset(vis,,sizeof(vis));
memset(head,,sizeof(head));
n=read();K=read();
if(!n) return ;
for(int i=;i<n;i++)
{
int u=read(),v=read(),w=read();
insert(u,v,w);
}
sum=n;f[]=inf;//sum:用sum-节点已统计的子树节点个数=以节点临时父节点为根的子树节点个数
//f 记录以x为根的最大的子树的大小,最后从f中取最小值
//f[0]=inf 不能删 因为每次getroot 更新root根据f[x]是否小于f[root],每次删除一个点root=0
getroot(,);//找第一个根 ,临时从第1个点开始找
work(root);
printf("%d\n",ans);
}
}
学习时打的注释
POJ 1741 Tree 求树上路径小于k的点对个数)的更多相关文章
- POJ 1741 Tree(点分治点对<=k)
Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Def ...
- POJ 1741 [点分治][树上路径问题]
/* 不要低头,不要放弃,不要气馁,不要慌张 题意: 给一棵有n个节点的树,每条边都有一个正权值,求一共有多少个点对使得它们之间路的权值和小于给定的k. 思路: <分治算法在树的路径问题中的应用 ...
- [TS-A1505] [清橙2013中国国家集训队第二次作业] 树 [可持久化线段树,求树上路径第k大]
按Dfs序逐个插入点,建立可持久化线段树,每次查询即可,具体详见代码. 不知道为什么,代码慢的要死,, #include <iostream> #include <algorithm ...
- POJ - 1741 Tree
DescriptionGive a tree with n vertices,each edge has a length(positive integer less than 1001).Defin ...
- POJ 1741.Tree and 洛谷 P4178 Tree-树分治(点分治,容斥版) +二分 模板题-区间点对最短距离<=K的点对数量
POJ 1741. Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 34141 Accepted: 11420 ...
- POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)
Description A substring of a string T is defined as: T( i, k)= TiTi+1... Ti+k-1, 1≤ i≤ i+k-1≤| T|. G ...
- POJ1741--Tree (树的点分治) 求树上距离小于等于k的点对数
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 12276 Accepted: 3886 Description ...
- poj 1741 Tree(树的点分治)
poj 1741 Tree(树的点分治) 给出一个n个结点的树和一个整数k,问有多少个距离不超过k的点对. 首先对于一个树中的点对,要么经过根结点,要么不经过.所以我们可以把经过根节点的符合点对统计出 ...
- POJ 1741.Tree 树分治 树形dp 树上点对
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 24258 Accepted: 8062 Description ...
随机推荐
- 网桥 以及 IEEE802.1D 生成树协议
(一)网桥 网桥是一个layer 2设备,能够连接两个不同的网段. 如图
- thinkphp 学习1-模型
1.用M()方法创建模型类 $model = M("configsettings"); 2.使用find()方法或select()方法返回结果集 find()只返回一行记录,sel ...
- 守护线程(Daemon Thread)
在Java中有两类线程:用户线程 (User Thread).守护线程 (Daemon Thread). 所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称 ...
- SOAP 缓存问题
今天在进行soap调用老是出错,去其他人的机器上试下,就好了,下面是从网上找到的原因 一开始不知道还有SOAP缓存.因为类文件改变了,重新生成了WSDL文件,测试运行,竟然不能通过.给我的第一感觉是W ...
- vue 大概流程(未完)
规划组件结构 编写对应路由 具体写每个组件功能
- Django知识总汇
基础 Django基础 Django基本命令 model系统 ORM基础 ORM字段和参数 ORM对数据库操作 ORM中介模型 ORM之其他骚操作 templates系统 模板语言 views系统 视 ...
- Python2X和Python3X的区别
python2X:源码重复不规范python3X:整合源码,更清晰简单优美. python2X:默认的编码是ascii (解决办法为第一行添加 : #-*- encoding:ut ...
- iOS 数据库sqlite完整增删改查操作
1: 创建数据库表格 1.1 — 表格创建使用一个数据库软件快速创建:软件大小14.3M; 下载地址:http://pan.baidu.com/s/1qWOgGoc; 表格创建-> 打开软件,点 ...
- Java变量初始化之后的默认值问题
1) 局部变量初始化(局部变量:函数.语句中的变量,只在所属区域内有效)局部变量声明后,Java虚拟机不会自动给它初始化为默认值.因此对于局部变量,必须经过显示的初始化,才能使用它.如果使用一个没有被 ...
- 【题解】HDU4336 Card Collector
显然,这题有一种很简单的做法即直接状压卡牌的状态并转移期望的次数.但我们现在有一个更加强大的工具——min-max容斥. min-max 容斥(对期望也成立):\(E[max(S)] = \sum_{ ...