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——线段树分治
首先感谢题解小哥,他在标算外又总结了三种做法. 此处仅提及最后一种做法. 首先考虑题目中要求的所有结点度数为奇数的限制. 对于每一个联通块,因为所有结点总度数是偶数,所以总结点数也必须是偶数的.即所有 ...
随机推荐
- html5 页面音频
1. html5 样式 <audio class="audioleft download" id="audVoice" type="audio/ ...
- dede手机访问网站跳转到手机端模板
如何手机访问的时候跳转到自己的手机端模板,这时候需要一个js跳转代码:当手机访问的时候直接跳转到手机端 那手机端前提要有手机端的模板 <script> if(navigator.platf ...
- 初始MongoDB------将MongoDB创建为Windows服务
上一遍我写的是关于基本的MongoDB的安装,可能不是很详细,也写得很不好,不过这次我们会详细的说说,如果将MongoDB部署在你的Windows电脑上. 1.配置环境变量 如果每次都要在CMD进入M ...
- Fiddler—重复发送一个请求的设置
https://jingyan.baidu.com/article/b2c186c829a85dc46ff6ff60.html 选中一个request——>Reissue Sequentaill ...
- Bookshelf 2(poj3628,01背包,dp递推)
题目链接:Bookshelf 2(点击进入) 题目解读: 给n头牛,给出每个牛的高度h[i],给出一个书架的高度b(所有牛的高度相加>书架高度b),现在把一些牛叠起来(每头牛只能用一次,但不同的 ...
- P2041 分裂游戏
P2041 分裂游戏 手推$n=3$是无解的,推断$n>=3$是无解的 证明略,这是道结论题. #include<iostream> #include<cstdio> # ...
- Scoop - 在Windows命令行上进行程序安装
2019-01-28 22:49:21 资料来源自Scoop官方网站以及github上的帮助文档 如果有疑惑或者觉得文章有错误请留言以帮助改正 补充内容(2019-04-09 21:11:36):不 ...
- gulp-file-include 合并 html 文件
gulp-file-include 是 gulp 插件,它提供了一个 include 方法让我们可以像后端模板那样把公共部分的页面导入进来. 安装依赖包(包括了 gulp-file-include 和 ...
- windows下通过navicat for mysql连接centos6.3-64bit下的MySQL数据库
一.centos下MySQL安装 按照命令依次安装以下文件: mysql-devel 开发用到的库以及包含文件 mysql mysql 客户端 mysql-server 数据库服务器 yum inst ...
- pace.js – 网页自动加载进度条插件
网站顶部的页面加载进度条是怎么实现的,页面的加载进度百分比,有时候获取是比较麻烦的,当然也可以利用一些优秀的JavaScript插件来实现,今天就为大家介绍这样子的一款插件:pace.js. [官方网 ...