[CF160D] Edges in MST

前言:

这题不需要树剖或 \(\text{Tarjan}\) 等 高级 的算法,只需线段树合并即可。

解题思路:

首先我们先随便建出一个最小生成树,树边比较难考虑,先考虑非树边。

对于一条非树边,它只可能是情况 \(2\) 或者 \(3\),这点是显然的。

假设这条边连接 \(x\)\(y\),边权为 \(w\),那么它可以被纳入最小生成树,当且仅当 \(x\)\(y\) 这条路径上的最大边权等于 \(w\),因为这样就可以把这条边换上去且不影响连通性。

对于一条树边,它只可能是情况 \(1\) 或者 \(3\),这点也是是显然的。

假设存在一条可以替换它的边连接 \(x\)\(y\),边权为 \(w\),那么成立的条件就是 \(x\)\(y\) 这条路径包含这条树边,且 \(w\) 等于这条树边的边权。

那么我们可以枚举每一条非树边 \((x,y,w)\),将 \(x\)\(y\) 这条路径上的每一条边都尝试更新其最小值为 \(w\),最后判断每一条边的最小值是否等于其边权。这一步可以使用树链剖分,但是我不会,所以线段树合并做法就此诞生!

我们对于每一个点开一个动态开点权值线段树,对于一次操作,在 \(x\)\(y\)\(lca\) 点的对应线段树上的位置减 \(2\),在 \(x\)\(y\) 对应线段树上的位置加 \(1\),最后只要扫一遍整棵树,从下往上合并,并求出每个点最小的 \(> 0\) 的位置判断即可。

时间复杂度 \(O(n\log n)\)

代码实现:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
struct Edge{
    int x, y, w, id;
    bool operator < (const Edge A) const {
        return w < A.w;
    }
}e[N];
int n, m, fa[N], res, rt[N][19], mx[N][19], dep[N], vis[N], ans[N];
vector<pair<int, int> > v[N];
int find(int x){
    if(x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}
void merge(int x, int y){
    x = find(x), y = find(y);
    fa[x] = y;
}
void kruskal(){
    sort(e + 1, e + 1 + m);
    for(int i = 1; i <= m; i++){
        if(find(e[i].x) == find(e[i].y)) continue;
        merge(e[i].x, e[i].y);
        res += e[i].w, vis[e[i].id] = 1;
        v[e[i].x].push_back(make_pair(e[i].y, e[i].w));
        v[e[i].y].push_back(make_pair(e[i].x, e[i].w));
    }
}
void dfs(int x, int fa){
    dep[x] = dep[fa] + 1;
    for(int i = 1; i <= 18; i++){
        rt[x][i] = rt[rt[x][i - 1]][i - 1];
        mx[x][i] = max(mx[x][i - 1], mx[rt[x][i - 1]][i - 1]);
    }
    for(auto e : v[x]){
        int y = e.first, w = e.second;
        if(y == fa) continue;
        mx[y][0] = w, rt[y][0] = x;
        dfs(y, x);
    }
}
pair<int, int> LCA(int x, int y){
    int maxn = 0;
    if(dep[x] > dep[y]) swap(x, y);
    for(int tmp = dep[y] - dep[x], i = 0; tmp; i++, tmp >>= 1)
        if(tmp & 1){
            maxn = max(maxn, mx[y][i]);
            y = rt[y][i];
        }
    if(x == y) return make_pair(x, maxn);
    for(int i = 18; i >= 0; i--){
        if(rt[x][i] != rt[y][i]){
            maxn = max(maxn, mx[x][i]);
            maxn = max(maxn, mx[y][i]);
            x = rt[x][i];
            y = rt[y][i];
        }
    }
    maxn = max(maxn, mx[x][0]);
    maxn = max(maxn, mx[y][0]);
    return make_pair(rt[x][0], maxn);
}
bool cmp(Edge A, Edge B){
    return A.id < B.id;
}
int root[N];
struct Segment{
    int t[N * 20], ls[N * 20], rs[N * 20], cnt;
    void pushup(int op){
        t[op] = t[ls[op]] + t[rs[op]];
    }
    void update(int l, int r, int &op, int x, int val){
        if(!op) op = ++cnt;
        if(l == r){
            t[op] += val;
            return;
        }
        int mid = (l + r) >> 1;
        if(mid >= x) update(l, mid, ls[op], x, val);
        if(mid + 1 <= x) update(mid + 1, r, rs[op], x, val);
        pushup(op);
        return;
    }
    int merge(int l, int r, int op1, int op2){
        if(!op1) return op2;
        if(!op2) return op1;
        if(l == r){
            t[op1] += t[op2];
            return op1;
        }
        int mid = (l + r) >> 1;
        ls[op1] = merge(l, mid, ls[op1], ls[op2]);
        rs[op1] = merge(mid + 1, r, rs[op1], rs[op2]);
        pushup(op1);
        return op1;
    }
    int query(int l, int r, int op){
        if(l == r) return l;
        int mid = (l + r) >> 1;
        if(t[ls[op]] > 0) return query(l, mid, ls[op]);
        else if(t[rs[op]] > 0) return query(mid + 1, r, rs[op]);
        else return 0; 
    }
}T;
map<pair<int, int>, int> mp;
void dfs1(int x, int fa, int w1){
    for(auto e : v[x]){
        int y = e.first, w = e.second;
        if(y == fa) continue;
        dfs1(y, x, w);
        T.merge(1, M - 10, root[x], root[y]);
    }
    if(x == 1) return;
    if(w1 == T.query(1, M - 10, root[x]))
        mp[make_pair(x, fa)] = mp[make_pair(fa, x)] = 2;
    else mp[make_pair(x, fa)] = mp[make_pair(fa, x)] = 1;
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++) fa[i] = i, root[i] = i;
    T.cnt = n;
    for(int i = 1; i <= m; i++){
        int x, y, w;
        cin >> x >> y >> w;
        e[i] = Edge{x, y, w, i};
    }
    kruskal();
    dfs(1, 1);
    sort(e + 1, e + 1 + m, cmp);
    for(int i = 1; i <= m; i++){
        if(!vis[i]){
            auto ow = LCA(e[i].x, e[i].y);
            int maxn = ow.second, lca = ow.first;
            if(maxn == e[i].w){
                T.update(1, M - 10, root[lca], e[i].w, -2);
                T.update(1, M - 10, root[e[i].x], e[i].w, 1);
                T.update(1, M - 10, root[e[i].y], e[i].w, 1);
                ans[i] = 2;
            }
        }
    }
    dfs1(1, 1, -1);
    for(int i = 1; i <= m; i++){
        if(vis[i])
            ans[i] = mp[make_pair(e[i].x, e[i].y)];
        if(ans[i] == 0)
            cout << "none" << endl;
        else if(ans[i] == 1) cout << "any" << endl;
        else cout << "at least one" << endl;
    }
    return 0;
}
posted @ 2025-02-21 20:08  _huangweiliang  阅读(93)  评论(0)    收藏  举报