uva 12161 Ironman Race in Treeland 点分治

本文深入探讨了点分治算法在解决树形结构问题中的应用,特别是在寻找最优路径时的效率提升。通过将无根树转化为有根树,利用递归和重心点分治策略,实现了复杂度为nlog^2n的高效算法。文章详细解释了算法步骤,包括如何通过DFS获取节点到根的路径信息,以及如何通过排序和二分查找优化路径选择过程。

题目大意:一棵节点数为n的树,每条边有一个长度l和一个花费d,求一条路径,使得路径的总花费小于给定的m,且总长度最大

还是点分治,将这个无根树转为有根树之后,一条路径要么完全在某一子树下,要么经过根,子树的问题可以递归解决,
现在看经过根的情况,按序处理每一棵子树v,通过dfs可以得到该子树v所有节点到根的路径长度和花费,然后需要得到子树v这个集合
每一个点和已处理过子树的集合u内每一个点两两组合以更新答案,观察到如果集合v中路径a的花费高于b,但是a的
长度却小于b的时候,a一定不会在最优解中,因此可以将a删除,删除之后按花费排序后的长度也是有序的,因此可以枚举u中每一个
节点,去v中二分花费,然后更新最优长度,这样每层的复杂度还是nlogn,按重心点分治得到的是logn层,所以总的复杂度是nlog^2n
 

#include <cstdio>
#include <vector>
#include <utility>
#include <algorithm>
#include <cstring>

using namespace std;
struct node {
    int v, d, l;
}; //保存树中的节点
const int INF = 0x3f3f3f3f;
const int maxn = 3e4 + 10;
int n, m, t, root, allnode, ans;
int maxson[maxn], sz[maxn], vis[maxn], depth[maxn], cost[maxn];
//maxson[i]: 去除节点i后得到的森林中节点数最多的树的节点
//sz:保存子树的节点数
//depth记录长度,cost记录花费
vector<pair<int, int> > left, right; //left为处理过的子树中点的集合,right为当前处理的子树的点的集合
vector<node> G[maxn]; //建图
//找重心
void find_root(int u, int fa) {
    sz[u] = 1;
    maxson[u] = 0;
    for (int i = 0; i < G[u].size(); i++) {
        node x = G[u][i];
        int v = x.v;
        if (v == fa || vis[v]) continue;
        find_root(v, u);
        sz[u] += sz[v];
        maxson[u] = max(maxson[u], sz[v]);
    }
    maxson[u] = max(maxson[u], allnode - sz[u]);
    if (maxson[root] > maxson[u]) root = u;
}
//计算子树所有节点的长度和花费
void dfs(int u, int fa) {
    sz[u] = 1;
    right.push_back({cost[u], depth[u]});
    for (int i = 0; i < G[u].size(); i++) {
        node x = G[u][i];
        int v = x.v;
        int d = x.d;
        int l = x.l;
        if (v == fa ||vis[v]) continue;
        depth[v] = depth[u] + l;
        cost[v] = cost[u] + d;
        dfs(v, u);
        sz[u] += sz[v];
    }
}

void calc(int u) {
    left.clear();
    left.push_back({0, 0});
    sz[u] = 1;
    for (int i = 0; i < G[u].size(); i++) {
        node x = G[u][i];
        int v = x.v;
        if (vis[v]) continue;
        right.clear();
        depth[v] = x.l;
        cost[v] = x.d;
        dfs(v, u);
        sz[u] += sz[v];
        sort(right.begin(), right.end());
        //这里没有显示删除,而是将需要删除的节点的路径和长度赋值为和上一个节点
        for (int j = 1; j < right.size(); j++) {
            if (right[j].second <= right[j - 1].second) {
                right[j].second = right[j - 1].second;
                right[j].first = right[j - 1].first;
            }
        }
        for (int j = 0; j < left.size(); j++) {
            if (left[j].first > m) continue;
            int pos = upper_bound(right.begin(), right.end(), pair<int, int>(m - left[j].first, INF)) - right.begin() - 1;
            if (pos < 0) continue;
            if (right[pos].second + left[j].second > ans) ans = right[pos].second + left[j].second;
        }
        for (int j = 0; j < right.size(); j++) {
            left.push_back(right[j]);
        }
    }
}

void solve(int u) {
    left.clear();
    right.clear();
    calc(u);
    vis[u] = 1;
    for (int i = 0; i < G[u].size(); i++) {
        node x = G[u][i];
        int v = x.v;
        if (vis[v]) continue;
        allnode = sz[v];
        root = 0;
        find_root(v, u);
        solve(root);
    }
}

int main() {
    scanf("%d", &t);
    int cas = 0;
    while (t--) {
        cas ++;
        scanf("%d%d", &n, &m);
        left.reserve(n);
        right.reserve(n);
        memset(vis, 0, sizeof(vis));
        maxson[0] = n;
        for (int i = 1; i <= n; i++) G[i].clear();
        int u, v, d, l;
        for (int i = 1; i < n; i++) {
            scanf("%d%d%d%d", &u, &v, &d, &l);
            G[u].push_back({v, d, l});
            G[v].push_back({u, d, l});
        }
        root = ans = 0;
        allnode = n;
        find_root(1, -1);
        solve(root);
        printf("Case %d: %d\n", cas, ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值