【树上ST表】Gym - 101889I Imperial roads

Step1 Problem:

给你 n 个点,m 条无向边,边有边权。
有 Q 个询问:
询问给出一条边:u, v,让你输出包含 u, v 这条边的最小生成树的花费。
数据范围:
2 <= n <= 1e5, n-1 <= m <= 2e5, 1 <= Q <= 1e5.

Step2 Ideas:

对于给定的图,找出最小生成树,和最小生成树边权和 sum。
询问边 u, v 在树上:输出 sum.
询问边 u, v 不在树上:找出树上路径 u->v 最大边权,输出 sum + 询问边 - 最大边权。
核心就是解决路径 u->v 最大边权问题
1:我们可以树剖,维护链的最大值。
2:求出 u, v 的 lca,u->lca 边权最大值 和 v->lca 边权最大值 取最大。
对于 2:我们就整个 ST 表,dp[u][i]:代表 u->fq[u][i] 边权最大值,其中 fq[u][i]: 代表 u 的第 (1<<i) 个祖先,其中第 1 个祖先是父亲。

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
struct node
{
    int u, v, val;
    bool operator < (const node &b) const {
        return val < b.val;
    }
};
node a[N];
vector<node> Map[N];
int fq[N][20], dep[N];
int dp[N][20], mn[N];
void dfs(int u, int f, int step, int v)
{
    fq[u][0] = f, dep[u] = step;
    dp[u][0] = v;// u->f 的边权
    for(int i = 1; i < 20; i++)//预处理 fq, dp.
    {
        fq[u][i] = fq[fq[u][i-1]][i-1];
        dp[u][i] = max(dp[u][i-1], dp[fq[u][i-1]][i-1]);
    }
    for(int i = 0; i < Map[u].size(); i++)
    {
        int to = Map[u][i].v, val = Map[u][i].val;
        if(to != f) {
            dfs(to, u, step+1, val);
        }
    }
}
int Lca(int x, int y)
{
    if(dep[x] > dep[y]) swap(x, y);
    for(int i = 0; i < 20; i++)
    {
        if((dep[y]-dep[x])>>i&1) {
            y = fq[y][i];
        }
    }
    if(x == y) return x;
    for(int i = 19; i >= 0; i--)
    {
        if(fq[x][i] != fq[y][i]) {
            x = fq[x][i];
            y = fq[y][i];
        }
    }
    return fq[x][0];
}
void get_mn()
{
    for(int len = 1; len < 100005; len++)
    {
        int k = 0;
        while((1<<(k+1)) <= len) k++;
        mn[len] = k;
    }
}
int path_Max(int x, int y)//y 是 x 的祖先
{
    int t = dep[x]-dep[y];
    if(t == 0) return 0;
    else {
        int f = x;//找到 y 往 x 方向第 (1<<mn[t]) 个点
        for(int i = 19; i >= 0; i--)
        {
            if(dep[fq[f][i]] - dep[y] >= (1<<mn[t])) {
                f = fq[f][i];
            }
        }
        return max(dp[x][mn[t]], dp[f][mn[t]]);//返回路径 x->y 边权最大值
    }
}
int f[N];
int Find(int x)
{
    if(x == f[x]) return x;
    else return f[x] = Find(f[x]);
}
void Merge(int x, int y)
{
    x = Find(x), y = Find(y);
    if(x != y) f[y] = x;
}
map< pair<int, int>, int > has;
int main()
{
    get_mn();
    int n, m;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++) f[i] = i;
    for(int i = 1; i <= m; i++)
    {
        scanf("%d %d %d", &a[i].u, &a[i].v, &a[i].val);
        has[make_pair(a[i].u, a[i].v)] = a[i].val;
        has[make_pair(a[i].v, a[i].u)] = a[i].val;
    }
    sort(a+1, a+1+m);
    int sum = 0, x, y;
    for(int i = 1; i <= m; i++)
    {
        x = Find(a[i].u), y = Find(a[i].v);
        if(x != y) {
            Merge(x, y);
            sum += a[i].val;
            Map[a[i].u].push_back((node){a[i].u, a[i].v, a[i].val});
            Map[a[i].v].push_back((node){a[i].v, a[i].u, a[i].val});
        }
    }
    memset(dp, 0, sizeof(dp));
    dfs(1, 1, 1, 0);
    int Q;
    scanf("%d", &Q);
    while(Q--)
    {
        scanf("%d %d", &x, &y);
        int val = has[make_pair(x, y)];
        int lca = Lca(x, y);
        printf("%d\n", sum+val-max(path_Max(x, lca), path_Max(y, lca)));
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值