题意:有一棵树,每条边有一个距离,求dis(u,v)<=k的点的对数

题解:树分治,对于一颗树上的两点,要么在同一颗子树上,要么在不同子树上,要么一个点是根,另一个在某一子树上,对于第一种情况我们可以通过递归来变成第二种或者第三种情况。我们对于某一颗子树来说我们先统计dis[u]+dis[v]<=k的点的对数,然后把该子树的所有子节点为根的这颗子树中dis[u]+dis[v]<=k的点的对数删去,因为在递归到后面会计算重复(这是第一种情况),所以这样就得到了该子树的满足条件的点对数,那么剩下就是如何分了,要想使分的次数最小,那么我们可以每次选取该子树的重心来作为根节点,然后,每次计算完之后删除根节点,再计算所有子树,计算重心还是通过树形dp来求解,

对于树分治来说,分为点分治和边分治,点分治就是每次找到重心,然后把重心删除掉,对分成的树之间统计路径信息,直到只剩一个点的情况

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<cassert>
#include<iomanip>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define C 0.5772156649
#define pi acos(-1.0)
#define ll long long
#define mod 1000000007
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1 using namespace std;
using namespace __gnu_cxx; const double g=10.0,eps=1e-;
const int N=+,maxn=+,inf=0x3f3f3f; struct edge{
int to,Next,c;
}e[maxn];
int cnt,head[N];
int sz[N],zx[N],dis[N];
int n,k;
bool vis[N];
void add(int u,int v,int c)
{
e[cnt].to=v;
e[cnt].c=c;
e[cnt].Next=head[u];
head[u]=cnt++;
}
void init()
{
cnt=;
memset(head,-,sizeof head);
memset(vis,,sizeof vis);
}
void dfssz(int u,int f,int &sum)
{
sum++;
sz[u]=;
for(int i=head[u];~i;i=e[i].Next)
{
int x=e[i].to;
if(x==f||vis[x])continue;
dfssz(x,u,sum);
sz[u]+=sz[x];
}
}
void dfszx(int u,int f,int sum,int &ans,int &id)
{
zx[u]=sum-sz[u];
for(int i=head[u];~i;i=e[i].Next)
{
int x=e[i].to;
if(x==f||vis[x])continue;
dfszx(x,u,sum,ans,id);
zx[u]=max(zx[u],sz[x]);
}
if(ans>zx[u])
{
ans=zx[u];
id=u;
}
}
int findzx(int root)//找子树的重心
{
int sum=;
dfssz(root,-,sum);
int ans=inf,id=;
dfszx(root,-,sum,ans,id);
return id;
}
void dfsdis(int u,int f,vector<int> &son)
{
for(int i=head[u];~i;i=e[i].Next)
{
int x=e[i].to;
if(x==f||vis[x])continue;
dis[x]=dis[u]+e[i].c;
dfsdis(x,u,son);
}
son.pb(dis[u]);
}
bool comp(int a,int b)
{
return dis[a]<dis[b];
}
int ok(vector<int> &son)
{
sort(son.begin(),son.end());
// for(int i=0;i<son.size();i++)
// cout<<son[i]<<endl;
// cout<<endl;
int ans=;
for(int i=;i<son.size();i++)
{
int p=upper_bound(son.begin(),son.end(),k-son[i])-son.begin();
if(p>)p--;
if(p<=i)continue;
// cout<<p<<endl;
ans+=p-i;
}
// cout<<ans<<"*****"<<endl;
return ans;
}
void dfsson(int u,int f,vector<int> &son)
{
son.pb(dis[u]);
for(int i=head[u];~i;i=e[i].Next)
{
int x=e[i].to;
if(x==f||vis[x])continue;
dfsson(x,u,son);
}
}
int solve(int root)
{
int p=findzx(root);//找该子树的重心
memset(dis,-,sizeof dis);
dis[p]=;
vector<int>son;
son.clear();
dfsdis(p,-,son);//求子树中各点到重心的距离
int ans=ok(son);//满足该子树的dis[x]+dis[y]<=k对数;
// if(root==3)cout<<ans<<" ----"<<endl;
for(int i=head[p];~i;i=e[i].Next)
{
int x=e[i].to;
if(vis[x])continue;
son.clear();
dfsson(x,p,son);
ans-=ok(son);
}
// if(root==3)cout<<ans<<" ****"<<endl;
vis[p]=;
for(int i=head[p];~i;i=e[i].Next)
{
int x=e[i].to;
if(vis[x])continue;
ans+=solve(e[i].to);
//if(x==3)cout<<solve(e[i].to)<<"......."<<endl;
}
// if(root==3)cout<<p<<" "<<ans<<"+++++++++"<<endl;
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie();
while(~scanf("%d%d",&n,&k))
{
if(!n&&!k)break;
init();
for(int i=;i<n;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
printf("%d\n",solve());
}
return ;
}
/*******************
4 3
1 2 3
1 3 1
1 4 2
********************/

POJ1741 经典树分治的更多相关文章

  1. POJ-1741(树分治)

    树的点分治 给出详细的讲解!!点这里打开论文-分治算法在树的路径问题中的应用 本题目是他讲的第一个例题: 我的理解:每次都找树的重心,计算以重心为根的子树之间所贡献的答案.不断这样下去:如果这棵树是一 ...

  2. POJ1741 Tree 树分治模板

    http://poj.org/problem?id=1741   题意:一棵n个点的树,每条边有距离v,求该树中距离小于等于k的点的对数.   dis[y]表示点y到根x的距离,v代表根到子树根的距离 ...

  3. BZOJ4644: 经典傻逼题【线段树分治】【线性基】

    Description 这是一道经典傻逼题,对经典题很熟悉的人也不要激动,希望大家不要傻逼. 考虑一张N个点的带权无向图,点的编号为1到N. 对于图中的任意一个点集 (可以为空或者全集),所有恰好有一 ...

  4. [POJ1741]树上的点对 树分治

    Description 给一棵有n个节点的树,每条边都有一个长度(小于1001的正整数). 定义dist(u,v)=节点u到节点v的最短路距离. 给出一个整数k,我们称顶点对(u,v)是合法的当且仅当 ...

  5. POJ1741:Tree——题解+树分治简要讲解

    http://poj.org/problem?id=1741 题目大意:给一棵树,求点对间距离<=k的个数. ———————————————————— 以这道题为例记录一下对于树分治的理解. 树 ...

  6. 树分治&树链剖分相关题目讨论

    预备知识 树分治,树链剖分   poj1741 •一棵有n个节点的树,节点之间的边有长度.方方方想知道,有多少个点对距离不超过m 题解 点分治模板题.详见我早上写的http://www.cnblogs ...

  7. hdu4812 逆元+树分治

    逆元链接:https://www.cnblogs.com/zzqc/p/7192436.html 经典的树分治题 #pragma comment("linker,"/STACK:1 ...

  8. POJ 1741 树分治

    题目链接[http://poj.org/problem?id=1741] 题意: 给出一颗树,然后寻找点对(u,v)&&dis[u][v] < k的对数. 题解: 这是一个很经典 ...

  9. 树分治learning

    学习了树的点分治,树的边分治似乎因为复杂度过高而并不出众,于是没学 自己总结了一下 有些时候面对一些树上的结构 并且解决的是和路径有关的问题的时候 如果是多个询问 关注点在每次给出两个点,求一些关于这 ...

随机推荐

  1. CSDN论坛停站维护公告

    各位亲爱的用户:         CSDN论坛将于2014年11月9日23点至11月10日8点进行积分系统的升级,升级期间会影响大家的正常訪问和操作.给大家带来不便,敬请广大用户谅解.         ...

  2. SQL实现分页存储过程

    SQL分页存储过程的编写: --获得分页的DATASET资源 ALTER PROC sp_GetSource( @PageSize INT, --每页显示条数 @PageIndex INT, --页码 ...

  3. 一个用 C# 实现操作 XML 文件的公共类代码

    using System; using System.IO; using System.Data; using System.Xml; using System.Xml.XPath; namespac ...

  4. mysql用户管理(新增用户及权限管理)

    一.登录: # mysql  –u  root  –p  回车输入密码 退出: #exit; 二.修改密码: 格式:mysqladmin  –u 用户名 –p旧密码  password  新密码 # ...

  5. getchar,scanf以及缓冲区

    getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,也就是说,如果stdin有数据的话不用输入它就可以直接读取了.getch()和getche()是conio.h中的 ...

  6. javaweb学习纲要

    Java Web  概述 大纲: 1.C/S体系结构 2.B/S体系机构 3.两种体系结构比较 4.主流的Web程序应用平台 5.java web学习路线图 1.C/S体系结构 C/S是Client/ ...

  7. iOS URL Loading System / HTTP 重定向 认识与学习

    一个朋友问了我一个问题,需求是这样的:他要用本地的H5资源 替换 链接资源,  但是判断链接资源时候 因为一些操作请求本地化了之后  一些操作比如请求服务器使用的是http开头,然而本地资源一直是以f ...

  8. Linux查看某个端口+gcc动态编译

    Linux下就: 1.lsof -i:端口号 2.netstat -tunlp|grep 端口号 gcc:动态编译 gcc –fpic –c file.c –o file.o gcc –shared ...

  9. OpenGL学习进程(6)第四课:点、边和图形(一)点

    本节是OpenGL学习的第四个课时,下面介绍OpenGL点的相关知识:     (1)点的概念:     数学上的点,只有位置,没有大小.但在计算机中,无论计算精度如何提高,始终不能表示一个无穷小的点 ...

  10. TIJ读书笔记02-控制执行流程

      TIJ读书笔记02-控制执行流程 TIJ读书笔记02-控制执行流程 if-else 迭代 无条件分支 switch语句 所有条件语句都是以条件表达式的真假来决定执行路径,也就是通过布尔测试结果来决 ...