POJ 1741 Tree(经典点分)题解

来源:互联网 发布:qq软件管家最新版 编辑:程序博客网 时间:2024/05/28 15:36
青少年是一个美好而又是一去不可再得的时期,是将来一切光明和幸福的开端。——加里宁

这道题是经典的点分,拿来练练手。
题目大意:
一行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
原创粉丝点击