POJ 1741 树分治

题目链接【http://poj.org/problem?id=1741】

题意:

  给出一颗树,然后寻找点对(u,v)&&dis[u][v] < k的对数。

题解:

  这是一个很经典的树分治的题。假设我们选择了一个参考点u,那么对于不同的点对(u,v),(u , v)之间的路径有两种情况,经过点u,和不经过点u,加入我算出了没有经过点u的对数,然后把经过点u的加起来就是答案了,很简单,这就是分治的思想。具体看代码。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1e4 + 15;
int N, lit;
struct Edge
{
    int to, next, len;
    Edge() {}
    Edge(int to, int next, int len): to(to), next(next), len(len) {}
} E[maxn * 2];
int head[maxn], tot;
void initEdge()
{
    for(int i = 0; i <= N + 5; i++) head[i] = -1;
    tot = 0;
}
void addEdge(int u, int v, int len)
{
    E[tot] = Edge(v, head[u], len);
    head[u] = tot++;

    E[tot] = Edge(u, head[v], len);
    head[v] = tot++;
}
int vis[maxn];
int dep[maxn], L, R;
int sz[maxn];
void GetSize(int u, int fa)
{
    sz[u] = 1;
    for(int k = head[u]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(v == fa || vis[v]) continue;
        GetSize(v, u);
        sz[u] += sz[v];
    }
}
void GetRoot(int u, int fa, int tot, int &rt)
{
    int ma = tot - sz[u];
    if(ma > tot / 2) return ;
    for(int k = head[u]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(v == fa || vis[v]) continue;
        GetRoot(v, u, tot, rt);
        ma = max(ma, sz[v]);
    }
    if(ma <= tot / 2) rt = u;
}
void GetPath(int u, int fa, int len)
{
    dep[R++] = len;
    for(int k = head[u]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(v == fa || vis[v]) continue;
        GetPath(v, u, len + E[k].len);
    }
}
int GetNum(int L, int R)
{
    int ret = 0;
    int pos = R - 1;
    sort(dep + L, dep + R);
    for(int i = L; i < R; i++)
    {
        while(pos > i && dep[i] + dep[pos] > lit) pos--;
        if(pos > i) ret += pos - i;
        else break;
    }
    return ret;
}
int GetAns(int u)
{
    int ret = 0, rt = 0;
    GetSize(u, -1);
    GetRoot(u, -1, sz[u], rt);//找到重心
    vis[rt] = 1;//重心为分界点
    for(int k = head[rt]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(vis[v]) continue;
        ret += GetAns(v);//不过rt点的个数
    }
    L = R = 0;
    for(int k = head[rt]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(vis[v]) continue;
        GetPath(v, rt, E[k].len);
        ret -= GetNum(L, R);
        L = R;
    }
    ret += GetNum(0, R);
    for(int i = 0; i < R; i++)
        if(dep[i] <= lit) ret++;
        else break;
    vis[rt] = 0;
    return ret;
}
int main ()
{
    while(~scanf("%d %d", &N, &lit))
    {
        if(N == 0 && lit == 0) break;
        initEdge();
        for(int i = 1; i < N; i++)
        {
            int u, v, len;
            scanf("%d %d %d", &u, &v, &len);
            addEdge(u, v, len);
        }
        int ans = GetAns(1);
        printf("%d\n", ans);
    }
    return 0;
}

 

posted @ 2017-09-11 19:28  _Mickey  阅读(179)  评论(0编辑  收藏  举报