树上点对统计poj1741(树的点分治)
给定一棵树,边上有权值,要统计有多少对点路径的权值和<=k
分治算法在树的路径中的应用 这个论文里面有分析。
任意两点的路径,要么过根结点,要么在子树中。如果在子树中,那么只要递归处理就行了。
所以只要统计过根结点的对数。
所以只要算出任意结点到根结点的距离,用dist数组存下来,然后排序dist数组,就可以在O(n)的时间内算出有多少对点满足条件,具体见counts函数
但是counter函数计算时,会把路径都在子树中的情况也给加进来,所以需要再对相应的子树进行counts函数,然后减去。
然后在递归计算路径经过孩子结点的情况。
但是这样子的算法是会退化的,因为树可能是一条链。
所以就需要用到重心了。 因为重心具有的一个性质是删掉重心后,可以使得剩下的分支中,最大的最小。
所以在算出所有经过u的点对之后,递归计算v这个子树时,相当于计算一棵全新的树,所以只要找到这棵树的重心,继续计算就行了。
因为结点数可以每次都减少为原来的一般,所以层数只有logn层, 每层统计时,排序要nlogn,所以总的时间复杂度是n*logn*logn
- #pragma warning(disable:4996)
- #pragma comment(linker, "/STACK:1024000000,1024000000")
- #include <stdio.h>
- #include <string.h>
- #include <time.h>
- #include <math.h>
- #include <map>
- #include <set>
- #include <queue>
- #include <stack>
- #include <vector>
- #include <bitset>
- #include <algorithm>
- #include <iostream>
- #include <string>
- #include <functional>
- const int INF = << ;
- typedef __int64 LL;
- /*
- dist(u,v)为u,v两点路径上所有边的权和
- 如果dist(u,v)<=k,那么称(a,b)为合法点对
- 求合法点对的个数
- N<=10000, k<=10^9
- depth(i) + depth(j) <=k 且belong(i)!=belong(j)
- 我的问题是? 分治的时候,如何将树两边的点合起来???
- */
- const int N = + ;
- struct Node
- {
- int to, next, dis;
- }g[N];
- int head[N], e;
- int n, k, ans;
- int size[N];
- int mins, total, root;
- int dist[N], p;
- int vis[N];
- int cnt[N];
- void addEdge(int u, int v, int dis)
- {
- g[e].to = v;
- g[e].dis = dis;
- g[e].next = head[u];
- head[u] = e++;
- }
- void dfsRoot(int u, int f)
- {
- size[u] = ;
- int mxs = ;
- for (int i = head[u]; i != -; i = g[i].next)
- {
- int v = g[i].to;
- if (v == f || vis[v]) continue;
- dfsRoot(v, u);
- size[u] += size[v];
- mxs = std::max(mxs, size[v]);
- }
- //下面是找重心的代码
- if(mxs < total - size[u])
- mxs = total - size[u];
- if (mxs < mins)
- mins = mxs, root = u;
- }
- void getDis(int u, int f, int dis)
- {
- dist[p++] = dis;//遍历没有访问过的点,将到根结点的距离存下来
- for (int i = head[u]; i != -; i = g[i].next)
- {
- int v = g[i].to;
- if (v == f || vis[v]) continue;
- getDis(v, u, dis + g[i].dis);
- }
- }
- int counts(int u, int dis)
- {
- int ret = ;
- p = ;
- getDis(u, , dis);//得到dist数组
- std::sort(dist, dist + p);//每访问一个重心都要排序一次,所以是n*logn*logn
- int l = , r = p - ;
- while (l < r)
- {
- //如果dist[l]+dist[r]<=k ,那么dist[l]+任意的点都是可行的
- if (dist[l] + dist[r] <= k)
- {
- ret += (r - l);
- l++;//判断下一个l
- }
- else//如果这个r不行,那么这个r与谁结合都是不行的
- r--;
- }
- return ret;
- }
- void go(int u)
- {
- //total存的是子树所有结点
- mins = INF, total = size[u] ? size[u] : n;
- dfsRoot(u, -);//找到重心
- u = root;
- vis[u] = true;
- cnt[u] = ;
- cnt[u] += counts(u, );//统计过这个点的点对
- //但是会发生一个错误就是可能统计了位于同一棵子树的点
- for (int i = head[u]; i != -; i = g[i].next)
- {
- int v = g[i].to;
- if (vis[v]) continue;
- cnt[u] -= counts(v, g[i].dis);//所有要减去位于同一棵子树的点
- go(v);
- }
- }
- int main()
- {
- while(scanf("%d%d", &n, &k),n+k)
- {
- int u, v, dis;
- e = ;
- memset(head, -, sizeof(head));
- for (int i = ;i < n;++i)
- {
- scanf("%d%d%d", &u, &v, &dis);
- addEdge(u, v, dis); addEdge(v, u, dis);
- }
- ans = ;
- size[] = ;
- memset(vis, , sizeof(vis));
- go();
- for (int i = ;i <= n;++i)
- ans += cnt[i];
- printf("%d\n", ans);
- }
- return ;
- }
树上点对统计poj1741(树的点分治)的更多相关文章
- 【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树的直径/LCA/树的点分治
题目描述 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤40000)条的不同的垂直或水平的道路连结着农场,道路的长度不超过1000.这些农场的分布就像下面的地图一样, 图中农场用F ...
- POJ1741——Tree(树的点分治)
1 /* *********************************************** 2 Author :kuangbin 3 Created Time :2013-11-17 1 ...
- POJ3659 Cell Phone Network(树上最小支配集:树型DP)
题目求一棵树的最小支配数. 支配集,即把图的点分成两个集合,所有非支配集内的点都和支配集内的某一点相邻. 听说即使是二分图,最小支配集的求解也是还没多项式算法的.而树上求最小支配集树型DP就OK了. ...
- 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)
Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...
- 【poj1741】Tree 树的点分治
题目描述 Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dis ...
- 【bzoj1316】树上的询问 树的点分治+STL-set
题目描述 一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. 输入 第一行两个整数n, p分别表示点的个数和询问的个数. 接下来n-1行每行 ...
- HDU4871 Shortest-path tree(最短路径树 + 树的点分治)
题目大概要先求一张边有权的图的根为1的最短路径树,要满足根到各点路径序列的字典序最小:然后求这棵最短路径树包含k个结点的最长路径的长度和个数. 首先先构造出这棵字典序最小的最短路径树..好吧,我太傻逼 ...
- 【bzoj4311】向量 线段树对时间分治+STL-vector维护凸包
题目描述 你要维护一个向量集合,支持以下操作: 1.插入一个向量(x,y) 2.删除插入的第i个向量 3.查询当前集合与(x,y)点积的最大值是多少.如果当前是空集输出0 输入 第一行输入一个整数n, ...
- HDU4812 D Tree(树的点分治)
题目大概说给一棵有点权的树,输出字典序最小的点对,使这两点间路径上点权的乘积模1000003的结果为k. 树的点分治搞了.因为是点权过根的两条路径的LCA会被重复统计,而注意到1000003是质数,所 ...
随机推荐
- ExtJs4 笔记(12) Ext.toolbar.Toolbar 工具栏、Ext.toolbar.Paging 分页栏、Ext.ux.statusbar.StatusBar 状态栏
本篇讲解三个工具栏控件.其中Ext.toolbar.Toolbar可以用来放置一些工具类操控按钮和菜单,Ext.toolbar.Paging专门用来控制数据集的分页展示,Ext.ux.statusba ...
- 每天一个JavaScript实例-推断图片是否载入完毕
<!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- cocos2dx之lua项目开发中MVC框架的简单应用
**************************************************************************** 时间:2015-03-31 作者:Sharin ...
- Access Violation at address 00000000.Read of address 00000000 解决办法
是数组越标或没有初始化某个对象之类的问题,搂住细细检查一下代码, 使用指针前未做检查,而这个指针未初始化. 可能是new后没有delete,这样出现溢出的可能性比较大 检查代码或者跟踪试试 使 ...
- oschina图形和图像工具开源软件
图形和图像工具开源软件 http://www.oschina.net/project/tag/181/imagetools?sort=view&lang=21&os=0
- IOS中的id与nil
1 id id和void *并非完全一样.在上面的代码中,id是指向struct objc_object的一个指针,这个意思基本上是说,id是一个指向任何一个继承了Object(或者NSObject) ...
- zoj 2822 Sum of Different Primes (01背包)
///给你n 求他能分解成多少个的不同的k个素数相加之和 ///01背包,素数打表 # include <stdio.h> # include <algorithm> # in ...
- Ubuntu14.04搭建android开发环境
一 下载ADT 官方下载地址:http://developer.android.com/sdk/index.html(须要FQ或者改动host) 二 解压 1 使用终端将下载的文件解压当前文件夹下: ...
- linux根据部署jenkins
1. Jenkins 下载 Jenkins 下载网址:http://jenkins-ci.org/ 2. Jenkins 安装 (1) 安装JDK JDK下载:http://www.oracle.co ...
- -bash: ./job.sh: /bin/sh^M: bad interpreter: 没有那个文件或目录
昨天在windows下用写字板写了个shell脚本,使用winscp上传到linux上运行的时候发现运行不了,提示-bash: ./job.sh: /bin/sh^M: bad interpreter ...