poj 1741 楼教主男人八题之中的一个:树分治
id=1741
Description
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
Input
The last test case is followed by two zeros.
Output
Sample Input
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
Sample Output
8
/**
poj 1741 楼教主男人八题之中的一个:树分治
题目大意:给定一个带权树,问有多少对点之间的距离之和不超过k
解题思路:典型的树分治算法。 (转自ACMonster)
最easy想到的算法是:从每一个点出发遍历整棵树,统计数对个数。 由于时间复杂度O(N^2)。明显是无法满足要求的。 对于一棵有根树, 树中满足要求的一个数对所相应的一条路径,必定是下面两种情况之中的一个:
1、经过根节点
2、不经过根节点。也就是说在根节点的一棵子树中
对于情况2,能够递归求解,下面主要来考虑情况1。 设点i的深度为Depth[i],父亲为Parent[i]。
若i为根,则Belong[i]=-1。若Parent[i]为根,则Belong[i]=i,否则Belong[i]=Belong[Parent[i]]。
这三个量都能够通过一次BFS求得。 我们的目标是要统计:有多少对(i,j)满足i<j。Depth[i]+Depth[j]<=K且Belong[i]<>Belong[j] 假设这样考虑问题会变得比較麻烦,我们能够考虑换一种角度:
设X为满足i<j且Depth[i]+Depth[j]<=K的数对(i,j)的个数
设Y为满足i<j,Depth[i]+Depth[j]<=K且Belong[i]=Belong[j]数对(i,j)的个数
那么我们要统计的量便等于X-Y 求X、Y的过程均能够转化为下面问题:
已知A[1],A[2],...A[m],求满足i<j且A[i]+A[j]<=K的数对(i,j)的个数 对于这个问题。我们先将A从小到大排序。
设B[i]表示满足A[i]+A[p]<=K的最大的p(若不存在则为0)。我们的任务便转化为求出A所相应的B数组。那么。若B[i]>i,那么i对答案的贡献为B[i]-i。
显然,随着i的增大,B[i]的值是不会增大的。利用这个性质,我们能够在线性的时间内求出B数组,从而得到答案。 综上。设递归最大层数为L,由于每一层的时间复杂度均为“瓶颈”——排序的时间复杂度O(NlogN),所以总的时间复杂度为O(L*NlogN) 然而。假设遇到极端情况——这棵树是一根链,那么任意切割势必会导致层数达到O(N)级别,对于N=10000的数据是无法承受的。因此,我们在每一棵子树中选择“最优”的点切割。所谓“最优”,是指删除这个点后最大的子树尽量小。这个点能够通过树形DP在O(N)时间内求出。不会添加时间复杂度。 这样一来,即使是遇到一根链的情况时,L的值也不过O(logN)的。 因此,改进后算法时间复杂度为O(Nlog^2N),能够AC。
*/
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
const int maxn=10015;
const int INF=0x3f3f3f3f;
int head[maxn],ip;
int siz[maxn],can[maxn],lst[maxn],d[maxn],fa[maxn];
int ans,tl,n,k,l1,l2; void init()
{
memset(head,-1,sizeof(head));
memset(can,0,sizeof(can));
ip=0;
ans=0;
} struct note
{
int v,w,next;
} edge[maxn*2]; void addedge(int u,int v,int w)
{
edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++;
} void dfs1(int u,int pre)
{
siz[u]=1;
lst[++tl]=u;
for(int i=head[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
if(v==pre||can[v])continue;
dfs1(v,u);
fa[v]=u;
siz[u]+=siz[v];
}
}
int getroot(int u,int pre)
{
tl=0;
dfs1(u,pre);
int pos,tmp=INF,d,y;
for(int i=1; i<=tl; i++)
{
d=0,y=lst[i];
for(int p=head[y]; p!=-1; p=edge[p].next)
{
int v=edge[p].v;
if(v==fa[y]||can[v])continue;
d=max(d,siz[v]);
}
if(y!=u)
d=max(d,siz[u]-siz[y]);
if(d<tmp)pos=y,tmp=d;
}
return pos;
} void dfs2(int u,int pre,int dis)
{
lst[++l1]=u;
d[u]=dis;
for(int i=head[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
if(v==pre||can[v])continue;
dfs2(v,u,dis+edge[i].w);
}
} int getans(int *a,int l,int r)
{
int j=r,ret=0;
for(int i=l; i<=r; i++)
{
while(d[a[i]]+d[a[j]]>k&&j>i)j--;
ret+=j-i;
if(j==i)break;
}
return ret;
} inline bool cmp(int i,int j)
{
return d[i]<d[j];
}
void work(int u,int pre)
{
int root=getroot(u,pre);
l1=l2=0;
for(int i=head[root]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
if(can[v]==0)
{
l2=l1;
dfs2(v,root,edge[i].w);
sort(lst+l2+1,lst+l1+1,cmp);
ans-=getans(lst,l2+1,l1);
}
}
lst[++l1]=root,d[root]=0;
sort(lst+1,lst+l1+1,cmp);
ans+=getans(lst,1,l1);
can[root]=1;
for(int i=head[root]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
if(can[v]==0)
work(v,root);
}
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
if(n==0&&k==0)break;
init();
for(int i=0; i<n-1; i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
addedge(y,x,z);
}
work(1,0);
printf("%d\n",ans);
}
return 0;
}
poj 1741 楼教主男人八题之中的一个:树分治的更多相关文章
- nyoj137 取石子(三) 楼教主男人八题之一
思路:一堆时,N态.两堆时,当两堆数量相同,P态,不同为N态.三堆时,先手可以变成两堆一样的,必胜N态. 此时可以总结规律:堆数为偶数可能且石子数都是两两相同的,为P态.分析四堆时,当四堆中两两数量一 ...
- poj 1737男人八题之一 orz ltc
这是楼教主的男人八题之一.很高兴我能做八分之一的男人了. 题目大意:求有n个顶点的连通图有多少个. 解法: 1. 用总数减去不联通的图(网上说可以,我觉得时间悬) 2. 用动态规划(数学递推) ...
- Cogs 1714. [POJ1741][男人八题]树上的点对(点分治)
[POJ1741][男人八题]树上的点对 ★★★ 输入文件:poj1741_tree.in 输出文件:poj1741_tree.out 简单对比 时间限制:1 s 内存限制:256 MB [题目描述] ...
- POJ1742 Coins(男人八题之一)
前言 大名鼎鼎的男人八题,终于见识了... 题面 http://poj.org/problem?id=1742 分析 § 1 多重背包 这很显然是一个完全背包问题,考虑转移方程: DP[i][j]表示 ...
- poj 1742(好题,楼天城男人八题,混合背包)
Coins Time Limit: 3000MS Memory Limit: 30000K Total Submissions: 33269 Accepted: 11295 Descripti ...
- poj 1743 男人八题之后缀数组求最长不可重叠最长重复子串
Musical Theme Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 14874 Accepted: 5118 De ...
- 博弈论(男人八题):POJ 1740 A New Stone Game
A New Stone Game Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 5694 Accepted: 3119 ...
- 新男人八题---AStringGame
终于完成进度男人1/8,为了这题学了sam= = 题意先有一个串,n个子串,两个人轮流每次在子串上加字符,要求加完后还是原串的子串,最后不能加的就是输者,求赢的人 解法:sam之后在构造的状态图上跑s ...
- 【做题】cf603E——线段树分治
首先感谢题解小哥,他在标算外又总结了三种做法. 此处仅提及最后一种做法. 首先考虑题目中要求的所有结点度数为奇数的限制. 对于每一个联通块,因为所有结点总度数是偶数,所以总结点数也必须是偶数的.即所有 ...
随机推荐
- Farseer.net轻量级开源框架 中级篇:事务的使用
导航 目 录:Farseer.net轻量级开源框架 目录 上一篇:Farseer.net轻量级开源框架 入门篇: Where条件的终极使用 下一篇:Farseer.net轻量级开源框架 中级篇: ...
- PHP 在表单POST提交后数据分页实现,非GET,解决只有第一页显示正确的问题
//PHP 在表单POST提交后数据分页实现,非GET,使用SESSION,分页代码部分不在详述,主要为POST后的 除第一页之外的显示问题 //以下为ACTION页面 内容,仅为事例,当判断到页面未 ...
- Bootstrap Datatable 简单的基本配置
$(document).ready(function() { $('#example').dataTable({ "sScrollX": "100%", ...
- document.mozFullScreen
非标准该特性是非标准的,请尽量不要在生产环境中使用它! 概述 返回一个布尔值,表明当前文档是否处于全屏模式. 语法 var isFullScreen = document.mozFullScreen ...
- vim之vimrc配置文件
""""""""""""""""&quo ...
- Python的伪造数据库:Faker
faker 是一个可以让你生成伪造数据的Python包,在软件需求.开发.测试过程中常常需要利用一些假数据来做测试,这种时候就可以使用 Faker 来伪造数据从而用来测试. 一.Faker安装 pip ...
- uploadify的简单使用
简单的图片上传: 1.进入官网下载uploadify插件:http://www.uploadify.com/download/ 2.导入uploadify插件提供的css样式和类库: <link ...
- Vscode下调试基于Homestead环境的Laravel框架
PS:最近在学Laravel框架,本机IDE是Vscode,因为Vscode是真的好用!今天突然想调试php代码了,于是疯狂地在网上查资料,经过一上午的不懈努力,终于成功了! 准备工作 首先环境要保证 ...
- (转载)不错的CSS写法
根据微信订阅号“设计达人”推送的文章,学到了如题知识.个人尝试了一下,感觉还不错.原文链接:http://mp.weixin.qq.com/s/g9TyBwB9xIi45TGwTBOLSQ. 字体 从 ...
- QT使用插件QAxWidget来展示web页面
要求:用qt版开发一个桌面程序,该程序有一个界面,用来显示一个采用silverlight开发的web页面. 分析:在qt中实现web显示,根据qt的版本和对应编译器的版本,有如下选择: (1)5.6以 ...