题意就是求树上距离小于等于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. Ubuntu rar的坑

    通过apt-get安装rar后,执行rar命令会有如下坑: rar: loadlocale.c:130: _nl_intern_locale_data: Assertion `cnt < (si ...

  2. ["1", "2", "3"].map(parseInt)

    为什么["1", "2", "3"].map(parseInt) 为 1,NaN,NaN; parseInt() parseInt() 函数 ...

  3. Arduino中数据类型转换 float/double转换为char 亲测好使,dtostrf()函数

    如何轻松玩转Arduino单片机,当我在进行数据转换的时候,遇到了问题,尝试了C语言和C++中的好多函数,都没有达到将float型数据转换为char型的目的.苦苦查阅资料后,终于找到了一个大神级函数! ...

  4. android java 知识点

    ublic,protected,friendly,private的访问权限如下: 关键字        当前类       同一package        子孙类       其他package p ...

  5. SqlSessionFactory

    源码: public interface SqlSessionFactory { SqlSession openSession(); SqlSession openSession(boolean va ...

  6. 关于Java虚拟机

    先占个坑,可以参考以下两篇文档来进行初步的学习 http://www.cnblogs.com/fingerboy/p/5456371.html http://www.importnew.com/244 ...

  7. JAVA解析XML的几种方法

    DOM DOM Document Object Model 文档对象模型.在应用程序中,基于DOM的解析器将一个XML文档转换成一个对象模型的集合(DOM树),应用程序正是通过对这个对象模型的操作,来 ...

  8. 固定table表头

    <style> #box{ height:214px; width:500px; overflow-y:auto;/** 必须,否则当表格数据过多时,不会产生滚动条,而是自动延长该div的 ...

  9. SQLite – GROUP BY

    SQLite - GROUP BY SQLite GROUP BY子句中使用与SELECT语句的合作安排相同的数据组. 在GROUP BY子句之前一个SELECT语句的WHERE子句,先于ORDER ...

  10. 使用python编写的简单远程管理软件

    因为用户可以选择是否同意被控制,所以并不算是木马. 使用python3.7,spyder,在windows 10 开发. client为控制端,server为被控端. 参考 mygithub http ...