POJ 1741/1987 树的点分治

树的点分治,主要思想是每次找子树的重心,计算经过根节点的情况数,再减去点对属于同一子树的情况。

  1 #include <iostream>
  2 #include <vector>
  3 #include <algorithm>
  4 #include <string>
  5 #include <string.h>
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8 #include <queue>
  9 #include <stack>
 10 #include <map>
 11 #include <set>
 12 #include <cmath>
 13 #include <ctime>
 14 #include <cassert>
 15 #include <sstream>
 16 using namespace std;
 17
 18 const int N=10010;
 19 struct Edge {
 20     int to,next;
 21     int w;
 22     Edge(){}
 23     Edge (int _t,int _n,int _w=0) {
 24         to=_t;
 25         next=_n;
 26         w=_w;
 27     }
 28 }edge[N<<1];
 29 int idx,head[N];
 30 void addEdge (int u,int v,int w) {
 31     ++idx;
 32     edge[idx]=Edge(v,head[u],w);
 33     head[u]=idx;
 34 }
 35 bool vis[N];
 36
 37 int K;// 输入中的k
 38
 39 int dis[N],disCnt;
 40 void getDis(int u,int f,int d) {
 41     dis[disCnt++]=d;
 42     for (int k=head[u];k;k=edge[k].next) {
 43         int v=edge[k].to;
 44         if (vis[v]||v==f) continue;
 45         getDis(v,u,d+edge[k].w);
 46     }
 47 }
 48
 49 int bal,cmp;
 50 int getBal(int u,int f) {
 51     int son=1;
 52     int dp=0;
 53     for (int k=head[u];k;k=edge[k].next) {
 54         int v=edge[k].to;
 55         if (vis[v]||v==f) continue;
 56         int subSon=getBal(v,u);
 57         son+=subSon;
 58         dp=max(dp,subSon);
 59     }
 60     dp=max(dp,disCnt-son);
 61     if (dp<cmp){
 62         cmp=dp;
 63         bal=u;
 64     }
 65     return son;
 66 }
 67
 68 int calc(int u,int initDis) {
 69     disCnt=0;
 70     getDis(u,-1,initDis);
 71     sort(dis,dis+disCnt);
 72     int ret=0;
 73     int l=0,r=disCnt-1;
 74     while (l<r) {
 75         if (dis[l]+dis[r]>K) r--;
 76         else ret+=r-l++;
 77     }
 78     return ret;
 79 }
 80
 81 int ans=0;
 82 void solve(int u) {
 83     cmp=~0U>>1;
 84     getBal(u,-1);
 85     ans+=calc(bal,0);
 86     vis[bal]=true;
 87     for (int k=head[bal];k;k=edge[k].next) {
 88         int v=edge[k].to;
 89         if (vis[v]) continue;
 90         ans-=calc(v,edge[k].w);// 减去子树内的重复计算的情况
 91         solve(v);
 92     }
 93 }
 94
 95 void init(int n) {
 96     idx=1;memset(head,0,sizeof head);
 97     memset(vis,false,sizeof vis);
 98     disCnt=n;// 由于在计算重心时,可以直接用之前算dis时的disCnt,所以这里初始化为n
 99     ans=0;
100 }
101 int main () {
102     int n;
103     while (scanf("%d %d",&n,&K)!=EOF) {
104         if (n==0&&K==0) break;
105         init(n);
106         for (int i=1;i<n;i++) {
107             int u,v,w;
108             scanf("%d %d %d",&u,&v,&w);
109             addEdge(u,v,w);
110             addEdge(v,u,w);
111         }
112         solve(1);
113         printf("%d\n",ans);
114     }
115     return 0;
116 }
时间: 2024-10-14 17:02:18

POJ 1741/1987 树的点分治的相关文章

poj 1741(树的点分治)

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

POJ 1741 Tree(树的点分治,入门题)

Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 21357   Accepted: 7006 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 in

POJ 1741 Tree 树的点分治

题目大意:给定一棵树,求树上距离不超过k的点对(x,y) (x<y)的数量 男人八题第五题...其实没那么难的说...比NOI2014最后一题好写多了0.0 首先两个点之间的路径有两种情况: 1.两点之间路径经过根 2.两点之间路径不经过根 首先讨论情况1 我们从根出发进行一次DFS,求出每个点到根的距离,排序,然后扫一遍数组O(n)出解 但其中如果两个点都属于根的同一棵子树,那么这两个点的路径一定是不经过根的,我们还要把这些点对减掉 于是枚举子树,同样把距离排序,扫数组即可 然后讨论情况2 既

Poj 1741 Tree (树的分治)

题目链接: Poj 1741 Tree 这个题目Tle的好苦啊,原来一直是树的重心没找对,Tle好长时间,终于对了,好感动,先贴个代码. 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 7 const int maxn = 10010; 8 struct node 9 { 10 int

POJ 1741 Tree 树的分治(点分治)

题目大意:给出一颗无根树和每条边的权值,求出树上两个点之间距离<=k的点的对数. 思路:树的点分治.利用递归和求树的重心来解决这类问题.因为满足题意的点对一共只有两种: 1.在以该节点的子树中且不经过该节点. 2.路径经过该节点. 对于第一种点,我们递归处理:第二种点,我们可以将所有子树的节点到这个子树的根节点的距离处理出来,然后排序处理出满足要求的点对的个数. 按照正常的树的结构来分割子树,这样的做法的时间复杂度肯定是不好看的,为了让子树大小尽量相同,我们每次处理这个子树前找到这个子树的重心,

POJ 1741 Tree 树+点分治

树的点分治 可以看09年漆子超论文,说的很详细. Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 12650   Accepted: 4025 Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance betwe

POJ 1741 Tree 树分治(点分治)

题意:给你一颗带边权树,问你其中 dis(v,u) <= k 的对数 解题思路: 首先推荐大家看 09年国家集训队漆子超 的论文 看到这题  我们可以有三种思路 第一种是枚举起点,遍历整颗树找对数    时间复杂度 为  O(n^2),空间复杂度为 O(n) 第二种是树形dp的思想     每个节点用 长度为 K 数组维护 ,递归求解  时间复杂度为 O(n ^k)空间复杂度 为 O(n) 第三种就是我们要用到的点分治的思想. 这种思想的具体思路是  先找到一个  根  对这个根进行 深搜, 找

POJ 1741 Tree(树分治)

去网上搜题解大多数都是说论文,搜了论文看了看,写的确实挺好,直接复制过来. 不过代码中有些细节还是要注意的,参考这篇http://blog.sina.com.cn/s/blog_6d5aa19a0100o73m.html一段 设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的过程均可以转化为以下

POJ 1741 Tree 树形DP(分治)

链接:id=1741">http://poj.org/problem?id=1741 题意:给出一棵树,节点数为N(N<=10000),给出N-1条边的两点和权值,给出数值k,问树上两点最短距离小于k的点对有多少个. 思路:拿到题的第一反应是LCA问题,只是细一想询问次数极限情况能够达到10000*5000次.即使用Tarjan也是超时没商议的. 2009年国家队论文提供了树的分治思想,对于本题就是树的分治的点分治的应用.每次找到能使含节点最多的子树的节点最少的根分而治之,相同方式分