http://poj.org/problem?id=1741

题目大意:给一棵树,求点对间距离<=k的个数。

————————————————————

以这道题为例记录一下对于树分治的理解。

树分治分为两类,一类是基于点的分治,一类是基于边的分治。

后者与树链剖分很相似,但是一般用不上,这里讲的是前者。

我们一般进行树分治找的点都是这棵树的重心(即子树最大者最小的点),我们每次操作都做与这个点相关的路径,然后删除这个点再重新寻找。

分重心的好处在于我们近似的将树分成了两份,类似于二分,其深度不超过O(logn)(其实有严格证明的,但是我太弱了,不会写)

分完重心的操作大致三种

1.找u,v,其中u,v在重心s的同一棵子树上(这种情况直接忽略,因为看下面的操作我们就能明白我们可以递归的完成这个操作)

2.找u,v,其中u,v在重心s的两棵子树上。

3.找u,查找u到重心s的路径。

我们发现3操作和2操作很相似,我们直接讨论2操作。

显然我们在2操作的路径当中不可避免的要经过s,所以我们从s开始bfs,求出每个点i到s的距离dis[i],我们的路径长度即为dis[u]+dis[v]。

3操作同理只是变成了dis[u]+dis[s],其中dis[s]=0.

这里提供一种简要算法:我们在求完dis之后对我们求的dis排序,这样我们就可以快速的求出点对距离<=k的个数。

但是这样就不可避免的要判重,为什么呢?

废话你这样排不就有可能把1操作的一部分点对先算了一遍,这样明显会导致答案变大。

那怎么办呢?我们对于每一棵子树,再删掉我们通过2操作得到的点对即可。

(现将s删掉,s的儿子dis[u]不变的情况下以u为起点bfs求点对,则这些点对就是在同一棵子树当中被计算的重复的点对,减去即可。)

#include<cmath>
#include<cstdio>
#include<queue>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=;
inline int read(){
int X=,w=; char ch=;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<)+(X<<)+(ch^),ch=getchar();
return w?-X:X;
}
struct node{
int w;
int to;
int nxt;
}edge[N*];
int cnt,n,k,head[N],q[N],dis[N],size[N],son[N],d[N],fa[N];
ll ans;
bool vis[N];
void add(int u,int v,int w){
cnt++;
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].nxt=head[u];
head[u]=cnt;
return;
}
int calcg(int st){
int r=,g,maxn=n;
q[++r]=st;
fa[st]=;
for(int l=;l<=r;l++){
int u=q[l];
size[u]=;
son[u]=;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(vis[v]||v==fa[u])continue;
fa[v]=u;
q[++r]=v;
}
}
for(int l=r;l>=;l--){
int u=q[l],v=fa[u];
if(r-size[u]>son[u])son[u]=r-size[u];
if(son[u]<maxn)g=u,maxn=son[u];
if(!v)break;
size[v]+=size[u];
if(size[u]>son[v])son[v]=size[u];
}
return g;
}
inline ll calc(int st,int L){
int r=,num=;
q[++r]=st;
dis[st]=L;
fa[st]=;
for(int l=;l<=r;l++){
int u=q[l];
d[++num]=dis[u];
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
int w=edge[i].w;
if(vis[v]||v==fa[u])continue;
fa[v]=u;
dis[v]=dis[u]+w;
q[++r]=v;
}
}
ll ecnt=;
sort(d+,d+num+);
int l1=,r1=num;
while(l1<r1){
if(d[l1]+d[r1]<=k){
ecnt+=r1-l1;
l1++;
}else r1--;
}
return ecnt;
}
void solve(int u){
int g=calcg(u);
vis[g]=;
ans+=calc(g,);
for(int i=head[g];i;i=edge[i].nxt){
int v=edge[i].to;
int w=edge[i].w;
if(!vis[v])ans-=calc(v,w);
}
for(int i=head[g];i;i=edge[i].nxt){
int v=edge[i].to;
if(!vis[v])solve(v);
}
return;
}
int main(){
while(scanf("%d%d",&n,&k)!=EOF&&n+k){
cnt=ans=;
memset(head,,sizeof(head));
memset(vis,,sizeof(vis));
for(int i=;i<n;i++){
int u=read();
int v=read();
int w=read();
add(u,v,w);
add(v,u,w);
}
solve();
printf("%lld\n",ans);
}
return ;
}

POJ1741:Tree——题解+树分治简要讲解的更多相关文章

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

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

  2. [POJ1741] Tree【树分治 点分治】

    传送门:http://poj.org/problem?id=1741 写的第一道树分治题,撒花纪念~ 对于每一对点对(i, j),它有三种情况: ① 其中一个是根节点.这种情况比较简单,直接加上就好了 ...

  3. [Luogu P4178]Tree 题解(点分治+平衡树)

    题目大意 给定一棵树,边带权,问有多少点对满足二者间距离$\leq K$,$n \leq 40000$. 题解 点分治专题首杀!$Jackpot!$ (本来看着题意比较简单想捡个软柿子捏,结果手断了… ...

  4. codechef Prime Distance On Tree(树分治+FFT)

    题目链接:http://www.codechef.com/problems/PRIMEDST/ 题意:给出一棵树,边长度都是1.每次任意取出两个点(u,v),他们之间的长度为素数的概率为多大? 树分治 ...

  5. POJ 1741 Tree【树分治】

    第一次接触树分治,看了论文又照挑战上抄的代码,也就理解到这个层次了.. 以后做题中再慢慢体会学习. 题目链接: http://poj.org/problem?id=1741 题意: 给定树和树边的权重 ...

  6. POJ 1741 Tree ——(树分治)

    思路参考于:http://blog.csdn.net/yang_7_46/article/details/9966455,不再赘述. 复杂度:找树的重心然后分治复杂度为logn,每次对距离数组dep排 ...

  7. [poj1741 Tree]树上点分治

    题意:给一个N个节点的带权树,求长度小于等于K的路径条数 思路:选取一个点作为根root,假设f(root)是当前树的答案,那么答案来源于两部分: (1)路径不经过root,那么就是完全在子树内,这部 ...

  8. POJ1741 Tree(树的点分治基础题)

    Give a tree with n vertices,each edge has a length(positive integer less than 1001).Define dist(u,v) ...

  9. POJ1741 Tree(树的点分治)

    题目给一棵边带权的树,统计路径长度<=k的点对数. 楼教主男人八题之一,分治算法在树上的应用. 一开始看论文看不懂,以为重心和距离那些是一遍预处理得来的..感觉上不敢想每棵子树都求一遍重心和距离 ...

随机推荐

  1. Altium designer18设置原理图尺寸

    1. AD18版本设置原理图尺寸和以前版本不一样,具体是在界面右侧Properties里面的Sheet Sizes.

  2. HTML <head>里面的标签

    <head> 中的标签可以引用脚本.指示浏览器在哪里找到样式表.提供元信息等等. 下面这些标签可用在 head 部分:<base>, <link>, <met ...

  3. 第一模块·开发基础-第1章 Python基础语法

    Python开发工具课前预习 01 Python全栈开发课程介绍1 02 Python全栈开发课程介绍2 03 Python全栈开发课程介绍3 04 编程语言介绍(一) 05 编程语言介绍(二)机器语 ...

  4. 【WXS数据类型】Array

    属性: 名称 值类型 说明 [Array].constructor [String] 返回值为“Array”,表示类型的结构字符串 [Array].length [Number] 返回数组长度 方法: ...

  5. js如何判断客户端是iOS还是Android等移动终端

    判断原理:JavaScript是前端开发的主要语言,我们可以通过编写JavaScript程序来判断浏览器的类型及版本.JavaScript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性来 ...

  6. 饥饿的小易(枚举+广度优先遍历(BFS))

    题目描述 小易总是感觉饥饿,所以作为章鱼的小易经常出去寻找贝壳吃.最开始小易在一个初始位置x_0.对于小易所处的当前位置x,他只能通过神秘的力量移动到 4 * x + 3或者8 * x + 7.因为使 ...

  7. HADOOP docker(三):HDFS高可用实验

      前言1.机器环境2.配置HA2.1 修改hdfs-site.xml2.2 设置core-site.xml3.配置手动HA3.1 关闭YARN.HDFS3.2 启动HDFS HA4.配置自动HA4. ...

  8. php分页类的实现与调用 (自我摘记)

    page.class.php <?php namespace Component; class Page { private $total; //数据表中总记录数 private $listRo ...

  9. nodejs在linux环境下安装更新方式

    #检查是否已经安装 rpm -qa | grep python #查版本 python #最好是重新安装 Python推荐版本( >= v2.5.0 & < 3.0.0 ),否则影 ...

  10. 自测之Lesson7:设备文件操作

    题目:请编写一个输入密码(不回显)的程序,要求通过设置终端来完成. 完成代码: #include <stdio.h> #include <unistd.h> #include ...