1. 程式人生 > >Where are you - 牛客

Where are you - 牛客

題意

\(n\)個點\(m\)條帶權邊的無向連通圖\(G\),記\(G\)的最小生成樹集合\(minTree\),求滿足\(edge \in minTree_i(1\leq i \leq size(minTree))\)的條數。(既這條邊要在\(G\)的所有最小生成樹中出現)

\(2 \leq n <m\leq2* 10^5\),\(1 \leq w \leq 10^6\)

題解

考慮相同權值的邊對最小生成樹造成的影響。將邊排序後,記\(w_i\)當前要加的邊的權值,\(G’\)為使用權值\(w_j(1\leq j \leq i - 1)\)的邊依照kruskal演算法構成的圖。那麼向\(G'\)

加入所有\(w_x = w_i\)的邊而構成的圖\(G''\)的橋就是出現在所有最小生成樹中的邊,然後不斷以遞推的形式就能求解。

1.\(G'\)可能有很多個,那麼選擇某個\(G'\)是否對\(G''\)的橋的個數會有影響?

​ 不會,記權值為\(w_i\)的邊的兩個端點為\(u,v\)\(G'\)的頂點集合為\(S\),如果\(u \in S, v \in S\),那麼無論是哪個\(G'\),這條邊都不會加入\(G'\)去構成\(G''\)。如果不都屬於\(S\),那麼這條邊與\(G'\)中邊沒有任何關係,既無論是哪個\(G'\)都不會對\(G''\)造成影響。

2.如果每加入一組權值為\(w_i\)

的邊,都求一次圖\(G''\)的橋,會不會超時?

​ 顯然會超時,由1得,新加的邊與\(G'\)沒有關係,所以可以將\(G'\)的各個連通分支壓縮成一個點。然後建圖,跑完Tarjan,再刪除原來的圖。

程式碼

const int N = 200050;

struct Edge { int u, v, w; } e[N];
struct node { int to, next, id; } G[N << 1];

int n, m, p, tot, cnt;
int fa[N], Low[N], Dfn[N], res[N], head[N];

bool cmp(Edge& a, Edge& b) { return a.w < b.w; }

void Inite() {
    cnt = tot = 0;
    mem(head, -1);
    rep(i, 1, N) fa[i] = i;
}

void addedge(int u, int v, int id) {
    G[tot].to = v, G[tot].id = id, G[tot].next = head[u], head[u] = tot++;
    G[tot].to = u, G[tot].id = id, G[tot].next = head[v], head[v] = tot++;
}

void Tarjan(int u, int last) {
    Low[u] = Dfn[u] = ++cnt;
    for (int i = head[u]; ~i; i = G[i].next) if (i != (1 ^ last)) {
        int v = G[i].to;
        if (Dfn[v]) Low[u] = min(Low[u], Dfn[v]);
        else {
            Tarjan(v, i);
            Low[u] = min(Low[u], Low[v]);
            if (Dfn[u] < Low[v]) res[G[i].id] = 1;
        }
    }
}

int Find(int a) {
    return (a == fa[a] ? a : (fa[a] = Find(fa[a])));
}

void Solve() {
    for (int i = 1, j = 1; i <= m; i = j, tot = 0) {
        while(e[j].w == e[i].w) ++j;
        rep(k, i, j) {
            int fu = Find(e[k].u), fv = Find(e[k].v);
            if (fu != fv) addedge(fu, fv, k);
        }
        rep(k, i, j) {
            int fu = Find(e[k].u), fv = Find(e[k].v);
            if ((fu != fv) && !Dfn[fu]) Tarjan(fu, -1);
        }
        rep(k, i, j) {
            int fu = Find(e[k].u), fv = Find(e[k].v);
            Dfn[fu] = Dfn[fv] = 0;
            head[fu] = head[fv] = -1;
            if (fu != fv) fa[fu] = fv;
        }
    }

    int ans = 0;
    Rep(i, 1, m) ans += res[i];
    pr(ans);
}

int main()
{
    Inite();
    
    IOS;

    cin >> n >> m >> p;
    Rep(i, 1, m) cin >> e[i].u >> e[i].v >> e[i].w;

    sort(e + 1, e + m + 1, cmp);

    Solve();
    return 0;
}