POJ 1741 树上 点的 分治
题意就是求树上距离小于等于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 树上 点的 分治的更多相关文章
- POJ 1741 树上的点分治
题目大意: 找到树上点对间距离不大于K的点对数 这是一道简单的练习点分治的题,注意的是为了防止点分治时出现最后分治出来一颗子树为一条直线,所以用递归的方法求出最合适的root点 #include &l ...
- poj 1741 树的点分治(入门)
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 18205 Accepted: 5951 Description ...
- POJ 1741 树的点分治
题目大意: 树上找到有多少条路径的边权值和>=k 这里在树上进行点分治,需要找到重心保证自己的不会出现过于长的链来降低复杂度 #include <cstdio> #include & ...
- POJ 1741 Tree 树的分治
原题链接:http://poj.org/problem?id=1741 题意: 给你棵树,询问有多少点对,使得这条路径上的权值和小于K 题解: 就..大约就是树的分治 代码: #include< ...
- POJ 1741 Tree【树分治】
第一次接触树分治,看了论文又照挑战上抄的代码,也就理解到这个层次了.. 以后做题中再慢慢体会学习. 题目链接: http://poj.org/problem?id=1741 题意: 给定树和树边的权重 ...
- poj 1741 Tree(点分治)
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 15548 Accepted: 5054 Description ...
- POJ 1741 Tree (树分治入门)
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 8554 Accepted: 2545 Description ...
- POJ 1741 Tree (点分治)
Tree Time Limit: 1000MS Memory ...
- POJ 1741 Tree 树的分治(点分治)
题目大意:给出一颗无根树和每条边的权值,求出树上两个点之间距离<=k的点的对数. 思路:树的点分治.利用递归和求树的重心来解决这类问题.由于满足题意的点对一共仅仅有两种: 1.在以该节点的子树中 ...
随机推荐
- 10.JAVA-接口、工厂模式、代理模式、详解
1.接口定义 接口属于一个特殊的类,这个类里面只能有抽象方法和全局常量 (该概念在JDK1.8之后被打破,在1.8后接口中还可以定义普通方法和静态方法,在后续章节会详讲) 1.1 接口具有以下几个原 ...
- Sql Server RowNumber和表变量分页性能优化小计
直接让代码了,对比看看就了解了 当然,这种情况比较适合提取字段较多的情况,要酌情而定 性能较差的: WITH #temp AS ( ...
- toast插件的简单封装(样式适用pc后台管理系统的场景)
直接分三个步骤吧: 1.手写一个toast.vue组件 <template> <transition name="toast-fade"> <div ...
- 如何在Ubuntu里安装Helm
Helm是什么?在战网上玩过暗黑破坏神2代的程序员们应该还记得,Helm是国度的意思. 而在计算机领域,Helm是什么? Helm是Kubernetes的一个包管理工具,有点像nodejs的npm,U ...
- 数据库系统概论(2)——Chap. 2 关系数据库基础
数据库系统概论(2)--Chap.2 关系数据库基础 一.关系数据结构及形式化定义 1.关系 关系模型的数据结构只包含单一的数据结构--关系.在关系模型中,现实世界的实体及实体间的各种联系均用单一的结 ...
- mybatis 存储过程的写法
(注意事项: 在使用游标的时候,不能在游标声明之前,使用crud) 存储过程示例 CREATE DEFINER=`root`@`::` PROCEDURE `earnings_proceduce`() ...
- javascript.json snippets vscode 注释
vscode vue js里面的注释 javascript.json { // Place your global snippets here. Each snippet is defined und ...
- 插入insert几种用法
1.insert ignore into 当插入数据时,如出现错误时,如重复数据,将不返回错误,只以警告形式返回.所以使用ignore请确保语句本身没有问题,否则也会被忽略掉.例如: INSERT I ...
- 【软件构造】第三章第五节 ADT和OOP中的等价性
第三章第五节 ADT和OOP中的等价性 在很多场景下,需要判定两个对象是否 “相等”,例如:判断某个Collection 中是否包含特定元素. ==和equals()有和区别?如何为自定义 ADT正确 ...
- Dubbo框架的说明
说实话,自己现在做的项目中有用到dubbo,但是我所负责的那一个模块,并没有涉及到dubbo,想学习一下dubbo,之前是没有学习完,这次继续... 一.背景知识总结 二.服务治理 三.Dubbo架构 ...