【算法练习】LightOJ - 1257 Farthest Nodes in a Tree (II) (点分治)

题意

求出树上所有点的最远距离。 n30000 n ≤ 30000

题解

点分治。
树上最长路径,要么经过当前的重心,要么不经过。所以可以点分治来做。
首先求出重心每一个子树上节点到重心的距离,同时求出每一个子树中到重心的最远点。对于一个子树上的节点,如果他的最长路径经过当前重心,那么一定是他到重心的距离+其他子树中到根节点的最远点的最大值;如果不经过,递归到子树求解。而重心的最远点则是到所有子树中最远点的最大值。
问题变成如何对于一个序列,如何快速求出某个元素除自己外的最大值。维护最大值和次大值即可。
然后更新答案,递归子树。

代码

#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 3e5+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
struct edge {
    int to, nxt, w;
}e[nmax << 1];
int head[nmax], tot;
int ans[nmax], tmpdis[nmax];
void add_edge(int u, int v, int w) {
    e[tot].to = v;
    e[tot].w = w;
    e[tot].nxt = head[u];
    head[u] = tot++;
}
int t, n;
int sz[nmax], root, nowmn, totnode;
bool visit[nmax];
int nowmx, thenode, id, fm, sm, fmid, smid, seq[nmax];
void getroot(int u, int f) {
    sz[u] = 1;
    int mxpart = 0;
    for(int i = head[u]; i != -1; i = e[i].nxt) {
        int v = e[i].to;
        if(v != f && !visit[v]) {
            getroot(v, u);
            sz[u] += sz[v];
            mxpart = max(mxpart, sz[v]);
        }
    }
    mxpart = max(mxpart, totnode - sz[u]);
    if(mxpart < nowmn) {
        nowmn = mxpart;
        root = u;
    }
}
void getans(int u, int f, int dist) {
    tmpdis[u] = dist;
    if(dist > nowmx) {
        nowmx = dist;
        thenode = u;
    }
    for(int i = head[u]; i != -1; i = e[i].nxt) {
        int v = e[i].to;
        if(!visit[v] && v != f) {
            getans(v, u, dist + e[i].w);
        }
    }
}
void update(int u, int f, int base) {
    ans[u] = max(ans[u], tmpdis[u] + base);
    for(int i = head[u]; i != -1; i = e[i].nxt) {
        int v = e[i].to;
        if(!visit[v] && v != f) {
            update(v, u, base);
        }
    }
}
void solve(int u) {
    visit[u] = true;
    id = 1;
    fm = fmid = sm = smid = 0;
    for(int i = head[u]; i != -1; i = e[i].nxt) {
        int v = e[i].to;
        if(!visit[v]) {
            seq[v] = id;
            nowmx = 0; thenode = 0;
            getans(v, u, e[i].w);
            if(fm == 0) {
                fm = nowmx;
                fmid = id;
            } else if(nowmx > fm) {
                sm = fm;
                smid = fmid;
                fm = nowmx;
                fmid = id;
            } else if(nowmx <= fm) {
                if(nowmx > sm) {
                    sm = nowmx;
                    smid = id;
                }
            }
            id++;
        }
    }
    ans[u] = max(ans[u], fm);
    for(int i = head[u]; i != -1; i = e[i].nxt) {
        int v = e[i].to;
        if(!visit[v]) {
            int tar = 0;
            if(seq[v] == fmid) tar = sm;
            else tar = fm;
            update(v, u, tar);
        }
    }
    for(int i = head[u]; i != -1; i = e[i].nxt) {
        int v = e[i].to;
        if(!visit[v]) {
            root = 0, nowmn = INF, totnode = sz[v];
            getroot(v, u);
            solve(root);
        }
    }
}
int main() {
//    freopen("out.txt", "w", stdout);
    scanf("%d", &t);
    for(int kase = 1; kase <=t ; ++kase) {
        scanf("%d", &n);
        tot = 0;
        for(int i = 0 ; i <= n; ++i) {
            ans[i] = 0;
            head[i] = -1;
            visit[i] = false;
        }
        int u, v, w;
        for(int i = 1; i < n; ++i) {
            scanf("%d %d %d", &u, &v, &w);
            u ++;
            v ++;
            add_edge(u, v, w);
            add_edge(v, u, w);
        }
        root = 0, nowmn = INF, totnode = n;
        getroot(1, -1);
        solve(root);
        printf("Case %d:\n", kase);
        for(int i = 1; i <= n; ++i)
            printf("%d\n", ans[i]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值