2021 上海ICPC Life is a Game(kruskal重构树,倍增)

2021 上海ICPC Life is a Game(kruskal重构树,倍增)

链接
题意:给出一个n个点m条边的无向图,每个点有点权,每条边有边权,第一次到达一个点可以获得这个点的点权,经过一条边需要当前总价值大于等于边权。对于每次询问,给出起点x和初始价值k,问这次能获得的最大权值是多大
思路:对于所有路径首先选择的边一定是最小生成树上的边,然后利用kruskal重构树的性质,详情见oi-wiki,建好新树后利用倍增处理好一个点往上的父节点和最大 w i w_i wi,方法类似LCA倍增,然后对于一个询问,不断往上跳,最后输出 k + a [ x ] k+a[x] k+a[x] a [ i ] a[i] a[i]代表到 i i i点的所有权值和,因为一定可以获取这个点往下的所有权值。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;
struct Edge1{int u, v, d;}e1[N];
int head[N << 3], idx, a[N << 1];
struct Edge{int to, nxt;}e[N << 3];
void add(int u, int v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
bool cmp(Edge1 _a, Edge1 b) {return _a.d < b.d;}
int f[N << 1];
int find(int n) {return n == f[n] ? n : f[n] = find(f[n]);}
int n, m, q;
int wi[N << 1], fa[N << 1][33], w[N << 1][33];
 
void dfs(int u, int _f)
{
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v == _f) continue;
        dfs(v, u);
        a[u] += a[v];
    }fa[u][0] = _f, w[u][0] = wi[_f] - a[u];
}

void kruskal()
{
    sort(e1 + 1, e1 + 1 + m, cmp);
    int cnt = n;
    for (int i = 1; i <= m; i++) {
        int x = e1[i].u, y = e1[i].v, d = e1[i].d;
        x = find(x), y = find(y);
        if (x == y) continue;
        cnt++;
        f[x] = cnt, f[y] = cnt;
        add(cnt, x); add(cnt, y); wi[cnt] = d;
    }
    dfs(cnt, 0);
    for (int i = 1; i <= 25; i++) {
        for (int j = 1; j <= cnt; j++) {
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
            w[j][i] = max(w[j][i - 1], w[fa[j][i - 1]][i - 1]);
        }
    }
}

signed main()
{
    cin >> n >> m >> q;
    for (int i = 1; i <= n + m; i++) f[i] = i;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= m; i++) {
        cin >> e1[i].u >> e1[i].v >> e1[i].d;
    }
    kruskal();
    while (q--) {
        int x, k;
        cin >> x >> k;
        for (int i = 24; i >= 0; i--) {
            if (fa[x][i]) {
                if (w[x][i] <= k) {x = fa[x][i];}
            }
        }cout << k + a[x] << endl;
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值