【poj1741】Tree 树的点分治
题目描述
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.
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.
输入
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.
The last test case is followed by two zeros.
输出
For each test case output the answer on a single line.
样例输入
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
样例输出
8
题目大意
多组测试数据,每次输入n、m,和一棵n个点的有边权的树,问你满足x到y距离小于等于m的无序点对(x,y)的个数是多少。
题解
树的点分治模板题,第一次写
考虑到路径只有两种情况,一是经过根节点,二是不经过根节点。
如果不经过根节点,那么一定经过最小公共子树的根节点,可以转化为问题一的子问题。
于是考虑怎么递归解决问题一。
对于根节点进行一次dfs,求出deep,并将其从小到大排序。
避免重复,只需要求出其中deep[x]≤deep[y]且deep[x]+deep[y]≤m的个数。
用i表示左指针,j表示右指针,i从左向右遍历。
如果deep[i]+deep[j]≤m,则点对(i,t)(i<t≤j)都符合题意,将j-i加入答案中,并且i++;否则j--。
然而这样还会重复计算在同一棵子树中的点对,所以再进行下一步dfs之前需要减去重复部分。
但是这样做会TLE。为什么?因为树可能会退化,导致选择链头时时间复杂度极大。
于是每次不能固定选择root,而是以重心作为root去处理,这样能保证时间复杂度再O(nlog2n)以下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | #include <cstdio> #include <cstring> #include <algorithm> #define N 10010 using namespace std; int m , head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , si[N] , deep[N] , root , vis[N] , f[N] , sn , d[N] , tot , ans; void add( int x , int y , int z) { to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt; } void getroot( int x , int fa) { f[x] = 0 , si[x] = 1; int i; for (i = head[x] ; i ; i = next[i]) if (to[i] != fa && !vis[to[i]]) getroot(to[i] , x) , si[x] += si[to[i]] , f[x] = max(f[x] , si[to[i]]); f[x] = max(f[x] , sn - si[x]); if (f[root] > f[x]) root = x; } void getdeep( int x , int fa) { d[++tot] = deep[x]; int i; for (i = head[x] ; i ; i = next[i]) if (to[i] != fa && !vis[to[i]]) deep[to[i]] = deep[x] + len[i] , getdeep(to[i] , x); } int calc( int x) { tot = 0 , getdeep(x , 0) , sort(d + 1 , d + tot + 1); int i = 1 , j = tot , sum = 0; while (i < j) { if (d[i] + d[j] <= m) sum += j - i , i ++ ; else j -- ; } return sum; } void dfs( int x) { deep[x] = 0 , vis[x] = 1 , ans += calc(x); int i; for (i = head[x] ; i ; i = next[i]) if (!vis[to[i]]) deep[to[i]] = len[i] , ans -= calc(to[i]) , sn = si[to[i]] , root = 0 , getroot(to[i] , 0) , dfs(root); } int main() { int n , i , x , y , z; while ( scanf ( "%d%d" , &n , &m) && (n || m)) { memset (head , 0 , sizeof (head)); memset (vis , 0 , sizeof (vis)); cnt = 0 , ans = 0; for (i = 1 ; i < n ; i ++ ) scanf ( "%d%d%d" , &x , &y , &z) , add(x , y , z) , add(y , x , z); f[0] = 0x7fffffff , sn = n; root = 0 , getroot(1 , 0) , dfs(root); printf ( "%d\n" , ans); } return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架