【树分治】poj1741 tree
2016-05-27 20:16
495 查看
题目
Description
Give a tree with n vertices,each edge has a length(positive integer less than 1001).
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 input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
Sample Output
8
题目大意
给定无根树一颗,求满足u到v的距离小于等于k的(u,v)点对个数。
男人八题之一,点分治。
点分治,在一颗无根树中找到树的重心(重心就是树中的一点,满足这一点的所有子节点中size值最大的最小),然后把重心去掉,对于重心的每一个子树进行相同操作。
这道题就是对于每一个子树,统计一下在根节点的不同子树中的两点对个数即可(相同子树中的点对分治到子树中操作即可)
先建图:
dfs找size:
然后dfs找root:
然后再写一个dfs求两点距离:
然后写主dfs分治:
还有一个是计算刚才说的跨过根节点的点对个数:
然后就完事了。。。对于我这个dfs写的很low的人,写四个dfs也是醉了。
直接贴全代码:
原po 传送门
Description
Give a tree with n vertices,each edge has a length(positive integer less than 1001).
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 input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
Sample Output
8
题目大意
给定无根树一颗,求满足u到v的距离小于等于k的(u,v)点对个数。
男人八题之一,点分治。
点分治,在一颗无根树中找到树的重心(重心就是树中的一点,满足这一点的所有子节点中size值最大的最小),然后把重心去掉,对于重心的每一个子树进行相同操作。
这道题就是对于每一个子树,统计一下在根节点的不同子树中的两点对个数即可(相同子树中的点对分治到子树中操作即可)
先建图:
struct edge{ int v,w,next; }e[MAXM]; void add_edge(int u, int v, int w) { e[edge].v = v; e[edge].w = w; e[edge].next = head[u]; head[u] = edge++; }
dfs找size:
void dfssize(int u,int fa){ size[u]=1; mx[u]=0; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(v!=fa&&!vis[v]){ dfssize(v,u); size[u]+=size[v]; if(size[v]>mx[u]) mx[u]=size[v]; } } }
然后dfs找root:
void dfsroot(int r,int u,int fa){ if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u]; if(mx[u]<minn) minn=mx[u],root=u; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(v!=fa&&!vis[v]) dfsroot(r,v,u); } }
然后再写一个dfs求两点距离:
void dfsdis(int u,int d,int fa){ dis[num++]=d; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(v!=fa&&!vis[v]) dfsdis(v,d+e[i].w,u); } }
然后写主dfs分治:
void dfsroot(int r,int u,int fa){ if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u]; if(mx[u]<minn) minn=mx[u],root=u; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].v; if(v!=fa&&!vis[v]) dfsroot(r,v,u); } }
还有一个是计算刚才说的跨过根节点的点对个数:
int calc(int u,int d){ int res=0; num=0; dfsdis(u,d,0); sort(dis,dis+num); int i=0,j=num-1; while(i<j){ while(dis[i]+dis[j]>k&&i<j) j--; res+=j-i; i++; } return res; }
然后就完事了。。。对于我这个dfs写的很low的人,写四个dfs也是醉了。
直接贴全代码:
#include <iostream>
#include <cstdio>
#include <vector>
#include <climits>
#include <cstring>
#include <algorithm>
#define MAXN 20000
#define MAXM 50000
using namespace std;
struct edge{
int v,w,next;
}e[MAXM];
int edge,ans,num,minn;
int size[MAXN],dis[MAXN],mx[MAXN],head[MAXN];
bool vis[MAXN];
int n,k;
int root;
void pre(){
memset(head,-1,sizeof head);
memset(vis,0,sizeof vis);
edge=ans=0;
}
void add_edge(int u, int v, int w)
{
e[edge].v = v;
e[edge].w = w;
e[edge].next = head[u];
head[u] = edge++;
}
void dfssize(int u,int fa){ size[u]=1; mx[u]=0; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[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]<minn) minn=mx[u],root=u; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[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!=-1;i=e[i].next){ int v=e[i].v; if(v!=fa&&!vis[v]) dfsdis(v,d+e[i].w,u); } }
int calc(int u,int d){ int res=0; num=0; dfsdis(u,d,0); sort(dis,dis+num); int i=0,j=num-1; while(i<j){ while(dis[i]+dis[j]>k&&i<j) j--; res+=j-i; i++; } return res; }
void dfs(int u){
minn=n;
dfssize(u,0);
dfsroot(u,u,0);
ans+=calc(root,0);
vis[root]=1;
for(int i=head[root];i!=-1;i=e[i].next){
int v=e[i].v;
if(!vis[v]){
ans-=calc(v,e[i].w);
dfs(v);
}
}
}
int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
if(!n && !k) break;
pre();
int u, v, w;
for(int i=0;i<n-1;i++)
{
scanf("%d%d%d", &u, &v, &w);
add_edge(u, v, w);
add_edge(v, u, w);
}
dfs(1);
printf("%d\n", ans);
}
return 0;
}
原po 传送门
相关文章推荐
- 初学ACM - 组合数学基础题目PKU 1833
- POJ ACM 1001
- POJ ACM 1002
- 1611:The Suspects
- POJ1089 区间合并
- POJ 2159 Ancient Cipher
- POJ 2635 The Embarrassed Cryptographe
- POJ 3292 Semi-prime H-numbers
- POJ 2773 HAPPY 2006
- POJ 3090 Visible Lattice Points
- POJ-2409-Let it Bead&&NYOJ-280-LK的项链
- POJ-1695-Magazine Delivery-dp
- POJ1523 SPF dfs
- POJ-1001 求高精度幂-大数乘法系列
- POJ-1003 Hangover
- POJ-1004 Financial Management
- POJ1050 最大子矩阵和
- 用单调栈解决最大连续矩形面积问题
- 2632 Crashing Robots的解决方法
- 1573 Robot Motion (简单题)