树分治
来源:互联网 发布:淘宝助手导入csv失败 编辑:程序博客网 时间:2024/05/16 00:46
参考:《分治算法在树的路径问题中的应用》漆子超
一、概述
树被定义为没有圈的联通图,具有以下几个性质:
1、在树中去掉一条边后所得的图是不连通的
2、在树中添加一条边后所得的图一定存在圈
3、树的每一顶点 u 和 v 之间有且仅有一条路径
分治算法在树结构上的运用,称之为树的分治算法
二、树的分治算法
1、基于点的分治
首先选取一个点将无根树转为有根树,再递归处理每一颗以根节点的儿子为根的子树
2、基于边的分治
在树中选取一条边,将原树分成两颗不相交的树,递归处理
3、
首先考虑如何选取点(边)
对于基于点的分治,我们选取一个点,要求将其删去后,结点最多的树的结点个数最小,这个点被称为“树的重心”
而基于边的分治,我们选取的边要满足所分离出来的两颗子树的结点个数尽量平均,这条边称为“中心边”
POJ 1655 Balancing Act
参考:http://blog.csdn.net/acdreamers/article/details/16905653
题意:
求树的重心的编号(如果有多个重心取编号最小)以及重心删除以后得到的最大子树的结点的个数
#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <set>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)#define lson low, mid, _id<<1#define rson mid+1, high, _id<<1|1typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 2e4 + 10;int T, N, edge, a, b, ans_id, min_size;int head[maxn], Next[2*maxn], to[2*maxn];int son_size[maxn];bool vis[maxn];void Init(void);void add_edge(int u, int v);void dfs(int u);int main(){#ifdef __AiR_H freopen("in.txt", "r", stdin);#endif // __AiR_H scanf("%d", &T); while (T--) { scanf("%d", &N); Init(); for (int i = 1; i < N; ++i) { scanf("%d %d", &a, &b); add_edge(a, b), add_edge(b, a); } dfs(1); printf("%d %d\n", ans_id, min_size); } return 0;}void dfs(int u){ vis[u] = true, son_size[u] = 0; int t = 0; for (int i = head[u]; i != -1; i = Next[i]) { int v = to[i]; if (!vis[v]) { dfs(v); son_size[u] += son_size[v] + 1; t = max(t, son_size[v]+1); } } t = max(t, N-son_size[u]-1); if (t < min_size || (t == min_size && u < ans_id)) { ans_id = u, min_size = t; }}void add_edge(int u, int v){ to[edge] = v, Next[edge] = head[u], head[u] = edge++;}void Init(void){ memset(vis, false, sizeof(vis)); memset(head, -1, sizeof(head)); edge = 0, min_size = INF;}
三、
例1、POJ 1741 Tree 树中点对统计
题意:
给定一棵 n(1 ≤ n ≤ 10000)个结点的边带权树,定义 dis(u, v) 为 u,v 两点间的最短路径长度,路径的长度定义为路径上所有边的权和
再给定一个 k(1 ≤ k ≤ 1e9),如果对于不同的两个结点 a,b,如果满足 dis(a, b) ≤ k,则称 (a, b) 为合法点对
求合法点对个数
解题思路:
记 dis[i] 表示点 i 到根节点的路径长度,belong[i] = x(x 为根节点的某个儿子,且结点 i 在以 x 为根的子树内)
那么我们要统计的就是:
满足 dis[i] + dis[j] ≤ k 且 belong[i] ≠ belong[j] 的 (i, j) 个数
= 满足 dis[i] + dis[j] ≤ k 的个数
- 满足 dis[i] + dis[j] ≤ k 且 belong[i] = belong[j] 的 (i, j) 个数
而对于这两个部分,都是要求满足 Ai + Aj ≤ k 的 (i, j) 的对数
将 A 排序后利用单调性我们容易得出一个 O(n) 的算法,所以我们可以用 O(nlogn) 的时间来解决这个问题
综上,此题使用树的分治算法时间复杂度为 O(n(logn)^2)
参考:http://hzwer.com/3460.html
#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <stack>#include <map>#include <set>#include <cmath>#include <cctype>#include <bitset>#include <ctime>using namespace std;#define REP(i, n) for (int i = 0; i < (n); ++i)#define lson low, mid, _id<<1#define rson mid+1, high, _id<<1|1typedef long long ll;typedef unsigned long long ull;typedef unsigned int uint;typedef pair<int, int> Pair;const ull mod = 1e9 + 7;const int INF = 0x7fffffff;const int maxn = 1e4 + 10;int n, k, a, b, l, edge, root, ans, sum;int head[maxn], Next[2*maxn], to[2*maxn], val[2*maxn];bool vis[maxn];int son_size[maxn], Max[maxn], dis_t[maxn], dis[maxn];void Init(void);void add_edge(int u, int v, int _val);void get_root(int u, int _father);void get_dis(int u, int _father);int cal(int u, int now);void slove(int u);int main(){#ifdef __AiR_H freopen("in.txt", "r", stdin);#endif // __AiR_H while (scanf("%d %d", &n, &k) != EOF && !(n == 0 && k == 0)) { Init(); for (int i = 1; i < n; ++i) { scanf("%d %d %d", &a, &b, &l); add_edge(a, b, l), add_edge(b, a, l); } get_root(1, 0); slove(root); printf("%d\n", ans); } return 0;}void slove(int u){ ans += cal(u, 0); vis[u] = true; for (int i = head[u]; i != -1; i = Next[i]) { int v = to[i]; if (!vis[v]) { ans -= cal(v, val[i]); sum = son_size[v] + 1; root = 0; get_root(v, root); slove(root); } }}int cal(int u, int now){ dis_t[u] = now, dis[0] = 0; get_dis(u, 0); sort(dis+1, dis+dis[0]+1); int ret = 0, low = 1, high = dis[0]; while (low < high) { if (dis[low] + dis[high] <= k) { ret += high - low, ++low; } else { --high; } } return ret;}void get_dis(int u, int _father){ dis[++dis[0]] = dis_t[u]; for (int i = head[u]; i != -1; i = Next[i]) { int v = to[i]; if (v == _father || vis[v]) { continue; } dis_t[v] = dis_t[u] + val[i]; get_dis(v, u); }}void get_root(int u, int _father) //求重心{ son_size[u] = 0, Max[u] = 0; for (int i = head[u]; i != -1; i = Next[i]) { int v = to[i]; if (v == _father || vis[v]) { continue; } get_root(v, u); son_size[u] += son_size[v] + 1; Max[u] = max(Max[u], son_size[v]+1); } Max[u] = max(Max[u], sum-son_size[u]-1); if (Max[u] < Max[root]) { root = u; }}void add_edge(int u, int v, int _val){ to[edge] = v, Next[edge] = head[u], val[edge] = _val, head[u] = edge++;}void Init(void){ memset(vis, false, sizeof(vis)); memset(head, -1, sizeof(head)); edge = ans = root = 0; Max[0] = INF; sum = n;}
- 树分治-点分治
- 树分治(点分治+边分治)
- 树分治
- 树分治
- 树分治
- 树的分治-点分治
- 【jzoj4715】【树上路径】【树】【分治】【点分治】
- POJ1741树的分治之点分治
- 树的分治
- POJ1655 【树的分治】
- hdu4670 树的分治
- 树分治+FFT
- 树|图 分治
- hdu4812(树分治)
- hdu 4812 树分治
- HDU 4916 树分治
- HDU 5102 树分治
- poj1741树分治
- linux-shell(8) grep 命令的使用【自己的笔记】
- android学习之路
- google GRPC for Ios Xcode 集成grpc 以及使用
- Effective C++ 0导读
- unity-使用 Camera 和 Render Texture 制作小地图
- 树分治
- eclipse丢失了Apache和svn插件
- mysql使用federated引擎实现dblink远程表访问
- JS:简单的拖拽事件和bug修复
- 状态模式
- 分享个免费的货币汇率API
- test
- React Native JS环境搭建
- [C语言] leetcode 119. Pascal's Triangle II