树分治

参考IOI2009《分治算法在树的路径中的应用》

1. 点分治

 算法框架

void getroot(int x, int fa) {
    sz[x] = 1, f[x] = 0;
    for(int i = 0; i < E[x].size(); i++) {
        int y = E[x][i].to;
        if(y == fa || vis[y]) continue;
        getroot(y, x);
        sz[x] += sz[y];
        f[x] = max(f[x], sz[y]);
    }
    f[x] = max(f[x], sum-sz[x]);    //sum为整个联通分量的大小,记录去除x后, 剩余联通分量的最大节点数
    if(f[x] < f[rt]) rt = x;        //更新当前重心
}

 

void solve(int x) { 
    vis[x] = 1;//将当前点标记
    for(int i = 0; i < E[x].size(); i++) {
        int y = E[x][i].to;
        if(vis[y]) continue ;
        sum = sz[y];//初始化sum为当前联通分量的大小
        getroot(x, rt = 0);//找连通块的重心
        solve(rt);//递归处理
    }
    vis[x] = 0;
}
int main() {
    build();//建树
    sum = f[0] = n;//初始化sum为当前联通分量的大小以及f[0], n个点
    getroot(1, rt = 0);//找重心
    solve(rt);//rt为重心, 点分治
}    

 

2. 边分治

 树中任意点的度数为D。当D为常数时,基于边分治的递归最坏情况下深度为O(logn)。通常D很小, 3~5左右。

=======================================================================================

POJ1741

统计树中有多少对节点距离 <= k,树的点分治+容斥,时间复杂度O(nlognlogn), 内含O(nlogn)排序

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<vector>
  6 using namespace std;
  7 
  8 const int N = 1e4+5;
  9 struct Edge {
 10     int to, w;
 11     Edge(int to, int w) : to(to), w(w){}
 12     Edge(){}
 13     bool operator < (const Edge &y) const {
 14         return w < y.w;
 15     }
 16 };
 17 vector<Edge> E[N];
 18 int sz[N], f[N], vis[N];
 19 int sum, rt;
 20 
 21 int d[N];
 22 int tmp[N], tot;
 23 
 24 int n, k, ans;
 25 
 26 void init() {//似乎不用init
 27     rt = ans = 0;
 28     for(int i = 0; i <= n; i++)
 29         E[i].clear();
 30     memset(f, 0, sizeof(f));
 31     memset(vis, 0, sizeof(vis));
 32     memset(sz, 0, sizeof(sz));
 33     memset(d, 0, sizeof(d));
 34     memset(tmp, 0, sizeof(tmp));
 35 }
 36 
 37 
 38 void getroot(int x, int fa) {
 39     sz[x] = 1, f[x] = 0;
 40     for(int i = 0; i < E[x].size(); i++) {
 41         int y = E[x][i].to;
 42         if(y == fa || vis[y]) continue;
 43         getroot(y, x);
 44         sz[x] += sz[y];
 45         f[x] = max(f[x], sz[y]);
 46     }
 47     f[x] = max(f[x], sum-sz[x]);    //sum为整个联通分量的大小,记录去除x后, 剩余联通分量的最大节点数
 48     if(f[x] < f[rt]) rt = x;        //更新当前重心
 49 }
 50 
 51 void getdeep(int x, int f) {
 52     tmp[tot++] = d[x];
 53     for(int i = 0; i < E[x].size(); i++) {
 54         int y = E[x][i].to, w = E[x][i].w;
 55         if(y == f || vis[y])continue;
 56         d[y] = d[x]+w;
 57         getdeep(y, x);
 58     }
 59 }
 60 int cal(int x, int now) {
 61     d[x] = now, tot = 0;
 62     getdeep(x, 0);
 63     sort(tmp,tmp+tot);
 64     int res = 0, l = 0, r = tot-1;
 65     while(l < r) {
 66         if(tmp[l]+tmp[r] <= k)
 67             res += r-l, l++;
 68         else r--;
 69     }
 70     return res;
 71 }
 72 void solve(int x) {
 73     ans += cal(x, 0);
 74     vis[x] = 1;
 75     for(int i = 0; i < E[x].size(); i++) {
 76         int y = E[x][i].to, w = E[x][i].w;
 77         if(vis[y]) continue ;
 78         ans -= cal(y, w);
 79         sum = sz[y];
 80         getroot(y, rt = 0);
 81         solve(rt);
 82     }
 83     vis[x] = 0;
 84 }
 85 int main() {
 86     while(~scanf("%d%d", &n, &k)) {
 87         if(n == 0&&k == 0) break;
 88         for(int i = 0; i <= n; i++) E[i].clear();
 89         for(int i = 1; i < n; i++) {
 90             int x, y, z;
 91             scanf("%d%d%d", &x, &y, &z);
 92             E[x].push_back(Edge(y, z));
 93             E[y].push_back(Edge(x, z));
 94         }
 95         ans = 0;
 96         sum = f[0] = n;//虚拟一个0节点, 但sum != n+1
 97         getroot(1, rt = 0);
 98         solve(rt);
 99         printf("%d\n",ans);
100     }
101 }
View Code

 

树形dp

链分治(树链剖分):修改点/链/子树,链支持快速合并,询问点/链/子树

点分治:树根不固定

DSU:树根固定

http://hzwer.com/5984.html

HDU4918

lbn

posted @ 2019-03-04 13:10  我在地狱  阅读(218)  评论(0编辑  收藏  举报