@wsndy-xx
2018-05-09T08:57:46.000000Z
字数 2731
阅读 1154
算法学习
之前貌似也接触过,不过,早忘干净了
顾名思义,点分治就是对树上的点分治,从而进行一些操作。将点拆开,实则将树拆分成许多子树进行处理,并不断进行。
考虑讲一个点进行分治,首先选点(这不是废话吗)。那么如何选点呢?
对比:如果树是一条链,选择链端和链心时间复杂度是明显不同的,前者时间复杂度为 O(n), 而后者为 O(logn).
不难发现,选择的点的子树越大,效率越低,那我们怎么选呢??
当然是树的重心啦
树的重心定义
树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点树最小。换句话说,删除这个点后最大连通块(一定是树)的结点数最小。(from 百度百科)
伪代码
size[] 节点的子树大小maxson[] 节点的最大子树的大小Size 这棵树(当前处理的子树)的大小Max 最大子树值Root 重心,以重心为根Getroot(u, fa){size[u] = 1, maxson[u] = 0;for(所有与 u 相连的点v) {if(未被访问 && v != fa) Getroot(v, u);size[u] += size[v];maxson[u] = max(maxson[u], size[v]);}maxson[u] = max(maxson[u], Size - size[u]);if(maxson[u] < Max) {Root = u, Max = maxson[u];}}
看一道非常非常经典的例题吧
poj1741
给一颗 个节点的树,每条边上有一个距离 。定义 为 到 的最小距离。给定 值,求有多少点对 使 到 的距离小于等于 。
可以这样考虑,先假定一个根 Root, 接下来求出所有经过该点的路径中长度 <= k 的路径个数
那怎么求呢??
对于一条树路径 只有经过或不经过一个点的情况,我们可以用总情况 - 所有不经过的情况就可以求出所有经过的情况啦
void Getans(int u) {Answer += Calc(u, 0);vis[u] = 1;for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(!vis[v]) {Answer -= Calc(v, G[i].w);Size = size[v];Max = oo;Getroot(v, 0);Getans(Root);}}return ;}
line 2 求出的是以 为根时的所有情况
line 7 减去了所有不经过 Root 的路径条数
然后递归处理,重新选 处理 的子节点
下面是一个计算函数
返回 的子树中所有点到 的距离 之后
任意两点之间距离 的点对个数
这里的 处理是为了减去重复答案时方便处理
显然 上一个函数 line 2 时 为
int Calc(int u, int len) {tot = 0;for(int i = 1; i <= n; i ++) dis[i] = 0;Getdis(u, 0, len);std:: sort(dis + 1, dis + tot + 1);int L = 1, R = tot, ret(0);while(L <= R) {if(dis[L] + dis[R] <= k) ret += (R - L), L ++;else R --;}return ret;}
#include <bits/stdc++.h>const int N = 1e4 + 10;const int oo = 1e9;struct Node {int v, w, nxt;} G[N << 1];int n, k, now = 1, head[N];int size[N], maxson[N], dis[N];bool vis[N];int Max, Root, tot, Size;int Answer;#define gc getchar()inline int read() {int x = 0; char c = gc;while(c < '0' || c > '9') c = gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;return x;}inline void Add(int u, int v, int w) {G[now].v = v; G[now].w = w; G[now].nxt = head[u]; head[u] = now ++;}void Getroot(int u, int fa) {size[u] = 1;maxson[u] = 0;for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(!vis[v] && v != fa) {Getroot(v, u);size[u] += size[v];maxson[u] = std:: max(maxson[u], size[v]);}}maxson[u] = std:: max(maxson[u], Size - size[u]);if(maxson[u] < Max) {Max = maxson[u]; Root = u;}}void Getdis(int u, int fa, int len) {dis[++ tot] = len;for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(!vis[v] && v != fa) Getdis(v, u, len + G[i].w);}}int Calc(int u, int len) {tot = 0;for(int i = 1; i <= n; i ++) dis[i] = 0;Getdis(u, 0, len);std:: sort(dis + 1, dis + tot + 1);int L = 1, R = tot, ret(0);while(L <= R) {if(dis[L] + dis[R] <= k) ret += (R - L), L ++;else R --;}return ret;}void Getans(int u) {Answer += Calc(u, 0);vis[u] = 1;for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(!vis[v]) {Answer -= Calc(v, G[i].w);Size = size[v];Max = oo;Getroot(v, 0);Getans(Root);}}return ;}int main() {while(1) {n = read(), k = read();if((! n) && (! k)) break;now = 1;for(int i = 1; i <= n; ++ i) head[i] = -1, vis[i] = 0;for(int i = 1; i < n; ++ i) {int u = read(), v = read(), w = read();Add(u, v, w), Add(v, u, w);}Answer = 0;Max = oo;Size = n;Getroot(1, 0);Getans(Root);std:: cout << Answer << "\n";}return 0;}