分治算法也可以在树上应用,详细可以看看《IOI2009 中国国家集训队论文 分治算法在树的路径问题中的应用 (漆子超) 》这篇论文。
这里主要对树的分治进行简单的总结。
树的分治快在哪?重点就在树的中心上,树的重心是指在某棵树中,把某个点去掉后,结点最多的子树的结点数最少。
这样,每次取重心来进行讨论,省下的递归处理,并且深度不超log n。
这一类题目往往是求某些合法路径数的,所以,只要考虑经过重心的路径树,问题就解决了。
要注意的是:
1.可以用bool 记录某个点是否已经计算过(即是否被当过重心),如果是删边的话可能会快些,但有些麻烦。
2.统计时可以采用容斥原理,这样问题可能会简单很多。然而,一般都是对于一个子树,统计它到前面已经统计过的子树的路径树,再把他们的信息合并起来,这样不会重复计算。
3.这类题目很多是要用到乘法原理的,注意数据规模,是否要用long long。
4.搜索用递归一般在10的5次方内不会溢出,而且简短不容易错。
下面给出整体的框架:
函数 Solve(当前点) {
计算每棵子树的大小;
当前点 = 重心;
标记某点已经成为重心(不要忘记);
统计经过重心的路径数;
递归求解(子树);
}
这里是poj 1741 和 USACO 2013US Open, Gold 第二题的代码:
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 10007
#define maxm 20007
int n, len;
int head[maxn], to[maxm], next[maxm], val[maxm], cnt;
void Insert(int u, int v, int p) {
cnt ++;
to[cnt] = v;
next[cnt] = head[u];
val[cnt] = p;
head[u] = cnt;
}
bool Init() {
scanf("%d%d\n", &n, &len);
if (n == 0 && len == 0)
return false;
cnt = 0;
memset(head, 0, sizeof(head));
for (int i = 1; i < n; i ++) {
int u, v, p;
scanf("%d%d%d\n", &u, &v, &p);
Insert(u, v, p);
Insert(v, u, p);
}
return true;
}
bool exist[maxn];
int size[maxn];
void Compute_Size(int u) {
exist[u] = false;
size[u] = 1;
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v]) {
Compute_Size(v);
size[u] += size[v];
}
}
exist[u] = true;
}
int Find_Root(int u, int tsize) {
exist[u] = false;
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v] && size[v] > tsize) {
v = Find_Root(v, tsize);
exist[u] = true;
return v;
}
}
exist[u] = true;
return u;
}
int A[maxn], An;
int B[maxn], Bn;
void Compute_B(int u, int tval) {
exist[u] = false;
Bn ++;
B[Bn] = tval;
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v])
Compute_B(v, tval + val[e]);
}
exist[u] = true;
}
int Solve(int u) {
Compute_Size(u);
u = Find_Root(u, size[u] >> 1);
exist[u] = false;
An = 0;
int res = 0;
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v]) {
Compute_B(v, val[e]);
sort(B + 1, B + 1 + Bn);
int j = An;
for (int i = 1; i <= Bn; i ++) {
while (j && B[i] + A[j] > len)
j --;
res += j;
if (B[i] <= len)
res ++;
}
for (; Bn; Bn --) {
An ++;
A[An] = B[Bn];
}
sort(A + 1, A + 1 + An);
}
}
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v])
res += Solve(v);
}
return res;
}
int main() {
while (Init()) {
memset(exist, true, sizeof(exist));
printf("%d\n", Solve(1));
}
return 0;
}
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define maxn 100007
#define maxm 200007
int n;
int head[maxn], to[maxm], val[maxm], next[maxm], cnt;
void Insert(int u, int v, int p) {
cnt ++;
to[cnt] = v;
next[cnt] = head[u];
val[cnt] = p;
head[u] = cnt;
}
void Init() {
scanf("%d\n", &n);
for (int i = 1; i < n; i ++) {
int u, v, p;
scanf("%d%d%d\n", &u, &v, &p);
if (! p) p = -1;
Insert(u, v, p);
Insert(v, u, p);
}
}
bool exist[maxn];
int size[maxn];
void Compute_Size(int u) {
exist[u] = false;
size[u] = 1;
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v]) {
Compute_Size(v);
size[u] += size[v];
}
}
exist[u] = true;
}
int Find_Root(int u, int tsize) {
exist[u] = false;
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v] && size[v] > tsize) {
int ret = Find_Root(v, tsize);
exist[u] = true;
return ret;
}
}
exist[u] = true;
return u;
}
#define maxc 200007
const int offset = 100000;
int A[maxc][2], Amin, Amax;
int B[maxc][2], Bmin, Bmax;
void Compute_B(int u, int value, int lmin, int lmax) {
Bmin = min(Bmin, value);
Bmax = max(Bmax, value);
if (value < lmin || value > lmax) {
lmin = min(lmin, value);
lmax = max(lmax, value);
B[value + offset][0] ++;
}
else
B[value + offset][1] ++;
exist[u] = false;
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v])
Compute_B(v, value + val[e], lmin, lmax);
}
exist[u] = true;
}
ll Solve(int u) {
Compute_Size(u);
u = Find_Root(u, size[u] >> 1);
exist[u] = false;
for (int i = Amin; i <= Amax; i ++)
A[i + offset][0] = A[i + offset][1] = 0;
Amin = offset, Amax = -offset;
ll res = 0LL;
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v]) {
for (int i = Bmin; i <= Bmax; i ++)
B[i + offset][0] = B[i + offset][1] = 0;
Bmin = offset, Bmax = -offset;
Compute_B(v, val[e], offset, -offset);
res += (ll) A[0 + offset][0] * B[0 + offset][0];
res += (ll) B[0 + offset][1];
for (int i = Bmin; i <= Bmax; i ++) {
res += (ll) A[-i + offset][0] * B[i + offset][1];
res += (ll) A[-i + offset][1] * B[i + offset][0];
res += (ll) A[-i + offset][1] * B[i + offset][1];
}
for (int i = Bmin; i <= Bmax; i ++) {
A[i + offset][0] += B[i + offset][0];
A[i + offset][1] += B[i + offset][1];
}
Amin = min(Amin, Bmin);
Amax = max(Amax, Bmax);
}
}
for (int e = head[u]; e; e = next[e]) {
int v = to[e];
if (exist[v])
res += Solve(v);
}
return res;
}
int main() {
freopen("yinyang.in", "r", stdin);
freopen("yinyang.out", "w", stdout);
Init();
memset(exist, true, sizeof(exist));
printf("%I64d\n", Solve(1));
return 0;
}