树路径中的分治算法 poj 1741

分治算法也可以在树上应用,详细可以看看《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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值