[题解]P11095 [ROI 2021] 旅行 (Day 2)

思路

对于 Subtask 2,本质是确定了最小值,要使 \(1 \leadsto u\) 路径上边权最大值最小,显然直接上 Kruskal 重构树。

对于 Subtask 3,本质是确定了最大值,要使 \(1 \leadsto u\) 路径上边权最小值最小,显然直接跑边双即可。

这启发我们考虑枚举一条边,让它成为最大值/最小值,容易发现枚举最大值是更有前途的。

将所有边按边权从小到大排序,依次枚举一条边,这条边就是当前的最大值,现在只能经过在这条边之前的边。

同时注意到一条边如果在 \(1 \leadsto u\) 的路径上,那么也一定在缩完边双后的 \(1 \leadsto u\) 的路径上。那么考虑加入一条连接 \((u,v)\) 的边带来的影响:

  1. \(u,v\) 已经在一个边双里面了,则没有任何影响,因为这条边一定比已经在边双中的所有边都大。
  2. \(u,v\) 已经在同一树里,但不在同一个边双内,则需要把 \(u \leadsto v\) 路径上所有点缩起来,这个点的点权是路径上所有点权和边权的最小值,然后更新子树内所有点的答案。
  3. \(u,v\) 不在同一个连通块内,直接把 \(u,v\) 连起来即可。

直接暴力处理这三种情况,复杂度是 \(\Theta(n^2)\) 的,考虑优化。

注意到第三种情况的边一定是最小生成树上的边,于是先跑出整个图的最小生成树,预处理得到每一个点的点权。

现在的问题是,某个点可能在还未与 1 连通的时候就记录了答案,因此我们需要在每个点与 \(1\) 所在连通块连接时,重新计算这个点的答案。

用两棵线段树,分别维护树上路径的最小值和实际的答案,需要支持区间 check min,单点修改和单点查询。

对于第二种情况的缩点操作可以用并查集维护,让并查集的根为连通块深度最小的点即可,复杂度是均摊的,总复杂度 \(\Theta((n + m) \log n)\)

Code

#include <bits/stdc++.h>
#define re register
#define fst first
#define snd second
#define chmin(a,b) (a = min(a,b))
#define chmax(a,b) (a = max(a,b))

using namespace std;

typedef pair<int,int> pii;
const int N = 3e5 + 10,M = N * 2;
const int inf = 2e9 + 10;
int n,m;
bool mark[N];
vector<int> S[N];
vector<pii> g[N];
int tim,fp[N],sz[N],dfn[N],pid[N],dep[N],ltm[N],val[N];

struct edge{
    int u,v,w;

    bool friend operator <(const edge &a,const edge &b){ return a.w < b.w; }
}E[N];

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

struct{
    int fp[N];

    inline void init(int n){
        for (re int i = 1;i <= n;i++) fp[i] = i;
    }

    inline int find(int x){
        if (fp[x] == x) return fp[x];
        else return fp[x] = find(fp[x]);
    }

    inline void merge(int a,int b){
        int x = find(a),y = find(b);
        fp[x] = y;
    }
}D;

inline void dfs(int u,int fa){
    dep[u] = dep[fp[u] = fa] + 1;
    sz[pid[dfn[u] = ++tim] = u] = 1;
    for (pii v:g[u]){
        if (v.fst == fa) continue;
        ltm[v.fst] = max(ltm[u],v.snd);
        val[v.fst] = min(val[u],E[v.snd].w);
        dfs(v.fst,u); sz[u] += sz[v.fst];
    }
}

struct{
    #define ls(u) (u << 1)
    #define rs(u) (u << 1 | 1)
    #define mid (tr[u].l + tr[u].r >> 1)

    int typ;

    struct node{
        int l,r;
        int val,tag;
    }tr[N << 2];

    inline void calc(int u,int k){ chmin(tr[u].val,k); chmin(tr[u].tag,k); }
    inline void pushup(int u){ tr[u].val = min(tr[ls(u)].val,tr[rs(u)].val); }

    inline void pushdown(int u){
        if (tr[u].tag < inf){ calc(ls(u),tr[u].tag); calc(rs(u),tr[u].tag); }
        tr[u].tag = inf;
    }

    inline void build(int u,int l,int r){
        tr[u] = {l,r,inf,inf};
        if (l == r) return (tr[u].val = (!typ ? val[pid[l]] : inf)),void();
        build(ls(u),l,mid); build(rs(u),mid + 1,r);
        pushup(u);
    }

    inline void modify(int u,int x,int k){
        if (tr[u].l == tr[u].r) return (tr[u].val = k),void();
        pushdown(u);
        if (x <= mid) modify(ls(u),x,k);
        else modify(rs(u),x,k);
        pushup(u);
    }

    inline void update(int u,int l,int r,int k){
        if (l <= tr[u].l && tr[u].r <= r) return calc(u,k);
        pushdown(u);
        if (l <= mid) update(ls(u),l,r,k);
        if (r > mid) update(rs(u),l,r,k);
        pushup(u);
    }

    inline int query(int u,int x){
        if (tr[u].l == tr[u].r) return tr[u].val;
        pushdown(u);
        if (x <= mid) return query(ls(u),x);
        else return query(rs(u),x);
    }

    #undef ls
    #undef rs
    #undef mid
}T1,T2;

int main(){
    n = read(),m = read();
    fill(val + 1,val + n + 1,inf);
    for (re int i = 1,u,v,w;i <= m;i++){
        u = read(),v = read(),w = read();
        E[i] = {u,v,w};
    } sort(E + 1,E + m + 1);
    D.init(n);
    for (re int i = 1,u,v;i <= m;i++){
        u = E[i].u,v = E[i].v;
        if (D.find(u) == D.find(v)) continue;
        g[u].push_back({v,i});
        g[v].push_back({u,i});
        mark[i] = true; D.merge(u,v);
    } dfs(1,0); D.init(n);
    T1.typ = 0,T2.typ = 1;
    T1.build(1,1,n); T2.build(1,1,n);
    for (re int i = 1;i <= n;i++) S[ltm[i]].push_back(i);
    for (re int i = 1,u,v,w;i <= m;i++){
        u = E[i].u,v = E[i].v,w = E[i].w;
        if (mark[i]){
            for (int x:S[i]) T2.modify(1,dfn[x],T1.query(1,dfn[x]) + w);
        }
        else{
            int x = D.find(u),y = D.find(v);
            if (x == y) continue;
            while (x != y){
                if (dep[x] < dep[y]) swap(x,y);
                int fa = D.find(fp[x]);
                chmin(val[fa],val[x]);
                D.merge(x,fa); x = fa;
            }
            T1.update(1,dfn[x],dfn[x] + sz[x] - 1,val[x]);
            T2.update(1,dfn[x],dfn[x] + sz[x] - 1,val[x] + w);
        }
    }
    for (re int i = 2;i <= n;i++) printf("%d\n",T2.query(1,dfn[i]));
    return 0;
}
posted @ 2025-09-21 10:49  WBIKPS  阅读(15)  评论(0)    收藏  举报