[POJ1741]Tree(点分治模板)
2017-12-29 16:44
453 查看
其实以前在求某段序列上的区间统计问题时就碰到过类似于这样的思想。
当时的区间统计问题思路大致是这样:
选取一个点作为中间点,从这个点的左边和右边统计出满足条件的点对。然后当前的中间点就可以删去了,接着递归统计左右两个区间的方案数。
其实这就是个分治和分类讨论的思想。
满足要求的解无非就是这两种:
1.区间包含中间点(也就是两端点在中间点的左右两边)
2.区间不包含中间点(也就是两个端点都在中间点的左边或右边)
所以正确性显而易见
淀粉质就是把这种思想应用于树上的路径统计上,选取一个点作为根,然后统计经过这个点的路径数,端点一定是在子树中的,
不过这会遇到一个问题,就是两个端点会在同一颗子树中,这就需要再统计子树中的答案再减去。接着再递归求解每一颗子树。
最后一个问题就是根节点的选取,需要选重心,防止出现链导致时间复杂度退化的情况。
其次是统计答案的方法,对于此题来说有个神奇的nlogn的方法,上面的链接中已经给出。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 100001 #define INF ~(1 << 31) using namespace std; int n, K, cnt, root, tot, ans; int head , to , nex , val , f , size , deep , d ; bool vis ; //f[i]表示节点i的最大子树的大小 //deep[i]表示节点i到根的距离 //vis[i] == 1 表示该节点删除 inline int read() { int x = 0, f = 1; char ch = getchar(); for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1; for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; return x * f; } inline void add(int x, int y, int z) { to[cnt] = y; val[cnt] = z; nex[cnt] = head[x]; head[x] = cnt++; } //获取当前块的重心 inline void getroot(int u, int fa) { int i, v; f[u] = 0; size[u] = 1; for(i = head[u]; ~i; i = nex[i]) { v = to[i]; if(v == fa || vis[v]) continue; getroot(v, u); size[u] += size[v]; f[u] = max(f[u], size[v]); } f[u] = max(f[u], tot - size[u]); if(f[u] < f[root]) root = u; } //获取当前块中所有的点到根的距离 inline void getdeep(int u, int fa) { int i, v; deep[++deep[0]] = d[u]; for(i = head[u]; ~i; i = nex[i]) { v = to[i]; if(v == fa || vis[v]) continue; d[v] = d[u] + val[i]; getdeep(v, u); } } //求解经过当前根的满足条件的路径的方案数 inline int cal(int u, int now) { int l, r, ret = 0; d[u] = now; deep[0] = 0; getdeep(u, 0); sort(deep + 1, deep + deep[0] + 1); l = 1, r = deep[0]; while(l < r) if(deep[l] + deep[r] <= K) ret += r - l++; else r--; return ret; } //递归求解每一块 inline void work(int u) { int i, v; vis[u] = 1; ans += cal(u, 0); for(i = head[u]; ~i; i = nex[i]) { v = to[i]; if(vis[v]) continue; //因为在求解的时候会遇到这种情况:经过当前根的满足条件的路径的两端点在同一颗子树中,这样的路径也会统计到答案中 //然而并不合法,所以需要遍历每一颗子树,减去每一颗子树中满足条件的路径 ans -= cal(v, val[i]); tot = size[v]; root = 0; getroot(v, u); work(root); } } int main() { int i, x, y, z; while(~scanf("%d %d", &n, &K)) { if(!n && !K) break; cnt = root = ans = 0; memset(vis, 0, sizeof(vis)); memset(head, -1, sizeof(head)); for(i = 1; i < n; i++) { x = read(); y = read(); z = read(); add(x, y, z); add(y, x, z); } tot = n; f[0] = INF; getroot(1, 0); work(root); printf("%d\n", ans); } return 0; }
相关文章推荐
- POJ1741 Tree(点分治模板题)
- 【POJ1741】Tree 树分治 模板咯?
- 【BZOJ】1468: Tree(POJ1741) 点分治
- POJ1741 [Tree] 点分治
- POJ1741 Tree(树的点分治基础题)
- POJ1741:Tree——题解+树分治简要讲解
- 【POJ 1741】【BZOJ 1468】【BZOJ 3365】【点分治模板题】Tree
- 树分治(点分治模板)poj-1741 Tree
- [poj1741 Tree]树上点分治
- 【树分治】poj1741 Tree
- Poj1741[Tree]题解--点分治||Treap
- 树分治(点分治模板)poj-1741 Tree
- 【poj1741】Tree 树的点分治
- [POJ1741]Tree |点分治
- [平衡树+启发式合并 || 点分治] POJ1741 Tree
- 【POJ1741】Tree(点分治)
- [POJ1741][JZOJ1166] 树中点对距离(点分治模板)
- [poj1741]Tree(点分治+容斥原理)
- 【poj1741】Tree 点分治
- POJ1741--Tree (树的点分治) 求树上距离小于等于k的点对数