POJ 1741 Tree(经典点分)题解

青少年是一个美好而又是一去不可再得的时期,是将来一切光明和幸福的开端。——加里宁

这道题是经典的点分,拿来练练手。
题目大意:
一行n,m范围是100000。(n是树的节点树木,m是树上链长度上限)
下面n-1行每行三个数给定树,(前两个给定树的边,后一个为长度)。
注意:是多组数据,最后两个0 0结束。
然后询问树上不超过m的链的个数。
Example:
IN PUT:
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
OUT PUT:
8
具体实现:
我们每次进行点分(求重心),然后对链进行处理,咋处理呢。
因为一条链必定在重心的两颗不同的子树内(易证得),所以大体思想是求出所有点到重心的距离,然后求出所有满足不大于m的链的个数,最后减去一个子树内的个数。
详见代码:

//
//  main.cpp
//  POJ 1741
//
//  Created by apple on 17/3/20.
//  Copyright © 2017年 apple. All rights reserved.
//

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100005;
struct Edge{
    int v, next, w;
};
Edge e[2 * N];
int n, m, head[N], num, x, y, z, vis[N], siz[N], f[N], sum, root, dis[N], tot, ans;
void adde( int i, int j, int w ) {
    e[++num].v = j;
    e[num].next = head[i];
    e[num].w = w;
    head[i] = num;
}
void init() {//初始化
    memset( head, 0, sizeof(head) );
    memset( vis, 0, sizeof(vis) );
    for ( int i = 1; i < n; i++ ) {
        scanf( "%d%d%d", &x, &y, &z );
        adde( x, y, z );
        adde( y, x, z );
    }
}
void getroot( int u, int fa ) {//求树的重心
    siz[u] = 1; f[u] = 0;
    for ( int i = head[u]; i; i = e[i].next ) {
        int v = e[i].v;
        if ( v == fa || vis[v] ) continue;
        getroot(v, u);
        siz[u] += siz[v];
        f[u] = max( f[u], siz[v] );
    }
    f[u] = max(f[u], sum-siz[u]);
    if ( f[u] < f[root] ) root = u;
}
void getdis( int u, int d, int fa ) {//求点到重心的距离
    dis[++tot] = d;
    for ( int i = head[u]; i; i = e[i].next ) {
        int v = e[i].v;
        if ( v == fa || vis[v] ) continue;
        getdis(v, d+e[i].w, u);
    }
}
int calc( int u, int d ) {//计算满足要求的个数
    int ret = 0;
    tot = 0;
    getdis(u, d, 0);
    sort(dis+1, dis+tot+1);
    int i = 1, j = tot;
    while ( i < j ) {//经典的双向
        if ( dis[i] + dis[j] <= m && i < j ) {
            ret += ( j - i );
            i++;
        }
        else j--;
    }
    return ret;
}
void solv( int u ) {
    ans += calc( u, 0 );//加上所有满足要求
    vis[u] = 1;
    for ( int i = head[u]; i; i = e[i].next ) {
        int v = e[i].v;
        if ( vis[v] ) continue;
        ans -= calc(v, e[i].w);//减去在同一个子树内的个数
        sum = siz[v];//求重心的初始化
        root = 0;
        getroot(v, 0);
        solv(root);//递归解决
    }
}
int main() {
    while (1) {
        ans = 0;
        scanf("%d%d", &n, &m);
        if ( n == 0 && m == 0 ) break;
        init();
        sum = n;
        root = 0;
        f[0] = 0x3f3f3f3f;
        getroot(1, 0);
        solv(root);
        printf( "%d\n", ans );
    }
    return 0;
}

完毕!
附上我对你的思念。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值