题意就是求树上距离小于等于K的点对有多少个

n2的算法肯定不行,因为1W个点

这就需要分治。可以看09年漆子超的论文

本题用到的是关于点的分治。

一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。

每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心

找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K,这里采用的方法就是把所有的距离存在一个数组里,进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。

最后的复杂度是n logn logn    其中每次快排是nlogn 而递归的深度是logn

友情链接:~~http://blog.csdn.net/sdj222555/article/details/7893862

 #include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define eps 1e-5
#define MAXN 11111
#define MAXM 55555
#define INF 1000000000
using namespace std;
struct EDGE
{
int v, next, w;
}edge[MAXM];
int head[MAXN], e;
int n, k, vis[MAXN], ans, root, num;
void init() // 清空初始值
{
memset(vis,,sizeof(vis));
memset(head,-,sizeof(head));
e=ans=;
}
void add(int u,int v,int w) // 边表 加边
{
edge[e].v=v;
edge[e].w=w;
edge[e].next=head[u];
head[u]=e++;
}
int mx[MAXN], size[MAXN], mi, dis[MAXN];
void dfssize(int u, int fa) //处理以u为顶的子树的大小 fa是其父节点
{
size[u] = ;
mx[u] = ;
for(int i = head[u]; i != -; i = edge[i].next)
{
int v = edge[i].v;
if(v != fa && !vis[v])
{
dfssize(v, u);
size[u] += size[v];
if(size[v] > mx[u]) mx[u] = size[v];
}
}
}
void dfsroot(int r,int u,int fa)//求重心所谓重心是指删去该点后
{ //所形成的子树的节点数最大的最小
if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u];
if(mx[u]<mi) mi=mx[u],root=u;
for(int i=head[u];i!=-;i=edge[i].next)
{
int v=edge[i].v;
if(v!=fa&&!vis[v]) dfsroot(r,v,u);
}
}
void dfsdis(int u, int d, int fa) //求所有点到达重心的距离 即dis
{
dis[num++] = d;
for(int i = head[u]; i != -; i = edge[i].next)
{
int v = edge[i].v;
if(v != fa && !vis[v]) dfsdis(v, d + edge[i].w, u);
}
}
int calc(int u,int d)
{
int ret=;
num=;
dfsdis(u,d,);
sort(dis,dis+num);
int i=,j=num-;
while(i<j) //经典
{
while(dis[i]+dis[j] > k && i < j) j--;
ret+=j-i;
i++;
}
return ret;
}
void dfs(int u)
{
mi = n;
dfssize(u, ); // 子树大小
dfsroot(u, u, ); // 重心 求完之后 root 即为重心
ans+=calc(root,);//经过root的并且满足要求的点对数(这时候会出现重边)
vis[root]=;
for(int i = head[root]; i != -; i = edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
{
ans-=calc(v,edge[i].w);//v是root的son 以v,edge[i].w继续向下深搜
//若这样还是满足要求(经过son并且满足要求的点对数,
//这就是重边的情况,这时将它减掉)
dfs(v);//继续处理root的son,情况同上
}
}
}
int main()
{
while(scanf("%d%d", &n, &k) != EOF)
{
if(!n && !k) break; // 不到终止条件
init();
int u, v, w;
for(int i = ; i < n - ; i++)
{
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
dfs();
printf("%d\n", ans);
}
return ;
}

附上图解:(图是手绘的,见谅)

当一遍处理的时候 ,root等于1,这时候ans会把5-1-2这样的点对加进去,所以我们以2,dis[2](dis[2]=1)再深搜,若是再这样的条件下还满足条件(过2节点,【此时dis值没变】,还满足小于等于7,说明他就是重边,应该减掉,而对于2-5这样合法的会在dfs[2]的时候,处理好)

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 11111
#define MAXM 55555
struct EDGE{
int v,w,next;
}edge[MAXM];
int n,k,vis[MAXN],mx[MAXN],e,ans;
int root,num,dis[MAXN],head[MAXN],size[MAXN],mi;
void init(){
memset(vis,,sizeof(vis));
memset(head,-,sizeof(head));
ans=e=;
}
void add(int u,int v,int w){
edge[e].v=v;edge[e].w=w;
edge[e].next=head[u];head[u]=e++;
}
void dfssize(int u,int fa){
size[u]=;mx[u]=;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].v;
if(v!=fa&&!vis[v]){
dfssize(v,u);
size[u]+=size[v];
if(size[v]>mx[u]) mx[u]=size[v];
}
}
}
void dfsroot(int r,int u,int fa){
if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u];
if(mx[u]<mi){ mi=mx[u];root=u; }
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].v;
if(v!=fa&&!vis[v]){
dfsroot(r,v,u);
}
}
}
void dfsdis(int u,int d,int fa){
dis[num++]=d;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].v;
if(v!=fa&&!vis[v]){
dfsdis(v,d+edge[i].w,u);
}
}
}
int calc(int u,int d){
int ret=;
num=;
dfsdis(u,d,);sort(dis,dis+num);
int i=,j=num-;
while(i<j){
while(dis[i]+dis[j]>k&&i<j) j--;
ret+=j-i;
i++;
}
return ret;
}
void dfs(int u)
{
mi=n;
dfssize(u,);dfsroot(u,u,);
ans+=calc(root,);
vis[root]=;
for(int i=head[root];i!=-;i=edge[i].next){
int v=edge[i].v;
if(!vis[v]){
ans-=calc(v,edge[i].w);
dfs(v);
}
}
}
int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
if(!n&&!k) break;
init();int u,v,w;
for(int i=;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
dfs();
printf("%d\n",ans);
}
return ;
}

另附网址:09年漆子超论文  提取密码:95tu

POJ 1741 树上 点的 分治的更多相关文章

  1. POJ 1741 树上的点分治

    题目大意: 找到树上点对间距离不大于K的点对数 这是一道简单的练习点分治的题,注意的是为了防止点分治时出现最后分治出来一颗子树为一条直线,所以用递归的方法求出最合适的root点 #include &l ...

  2. poj 1741 树的点分治(入门)

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 18205   Accepted: 5951 Description ...

  3. POJ 1741 树的点分治

    题目大意: 树上找到有多少条路径的边权值和>=k 这里在树上进行点分治,需要找到重心保证自己的不会出现过于长的链来降低复杂度 #include <cstdio> #include & ...

  4. POJ 1741 Tree 树的分治

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

  5. POJ 1741 Tree【树分治】

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

  6. poj 1741 Tree(点分治)

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

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

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

  8. POJ 1741 Tree (点分治)

                                                                        Tree Time Limit: 1000MS   Memory ...

  9. POJ 1741 Tree 树的分治(点分治)

    题目大意:给出一颗无根树和每条边的权值,求出树上两个点之间距离<=k的点的对数. 思路:树的点分治.利用递归和求树的重心来解决这类问题.由于满足题意的点对一共仅仅有两种: 1.在以该节点的子树中 ...

随机推荐

  1. 类似QQ在线离线好友界面

    把头像设置成圆形的代码如下: package com.example.lesson6_11_id19; import android.content.Context; import android.c ...

  2. 使用laravel的Command实现搜索引擎索引和模板的建立

    创建command,初始化es 创建成功后,可通过php artisan 查看到 php artisan make:command ESInit 安装guzzle composer require g ...

  3. 开启server-status失败

    近日在配置监控宝的apache监控老是出错,经过研究发现如下: 下面先做一些简要的介绍,以防以后查看之用. 一.server-status是什么?二.如何打开server-status?三.serve ...

  4. Javaweb学习笔记8—DBUtils工具包

    今天来讲javaweb的第8阶段学习. DBUtils技术,DBUtils是我们操作数据库很常用的功能,虽然后期使用都是它的封装结果,但是也需要掌握. 老规矩,首先先用一张思维导图来展现今天的博客内容 ...

  5. LibreOJ #101. 最大流

    题目描述 这是一道模板题. 给定 n nn 个点,m mm 条边,给定每条边的容量,求从点 s ss 到点 t tt 的最大流. 输入格式 第一行四个整数 n nn.m mm.s ss.t tt.接下 ...

  6. Android(java)学习笔记177: 服务(service)之音乐播放器

    1.我们播放音乐,希望在后台长期运行,不希望因为内存不足等等原因,从而导致被gc回收,音乐播放终止,所以我们这里使用服务Service创建一个音乐播放器. 2.创建一个音乐播放器项目(使用服务) (1 ...

  7. XtraBackUp 热备份工具

    是一款强大的在线热备份工具 备份的过程中,不锁表 使用percona-xtrabackup-24-2.4.7-1.el7.x86_64.rpm yum源安装: 1.安装Percona的库:       ...

  8. jeecms

    ===标签=== <!-- 显示一级栏目对应的二级栏目 --> <!-- [@cms_channel_list parentId=c.id] [#if tag_list?size&g ...

  9. DROP TRIGGER - 删除一个触发器定义

    SYNOPSIS DROP TRIGGER name ON table [ CASCADE | RESTRICT ] DESCRIPTION 描述 DROP TRIGGER 将删除所有对一个现存触发器 ...

  10. Linux常用的操作指令

    1.pwd-显示当前所在位置 2.cd-进入当前目录 3.cd..-返回上一级目录 4..ls命令参数选项有很多,ls也是经常使用到的命令.如果不清楚命令的使用方式可以直接 ls --help来查看 ...