1. 程式人生 > 其它 >[題解][CF-891C]Envy

[題解][CF-891C]Envy

\(\Longrightarrow\)原題連結

有的\(\mathrm{dalao}\)說這道題可以用虛樹寫,但是本人想不到。

考慮使用\(\mathrm{Kruskal}\)建立最小生成樹的過程,可以發現一個性質:當我們考慮了所有邊權\(\leq w\)的邊,無論我們使用的是哪些邊,構成的最小生成森林的連通性一定是一樣的。

也就是說,對於一個詢問中的權值不同的邊,可以分開判斷。

考慮判斷一些權值相同(假設為\(W\))的邊是否可以放到同一顆生成樹中。暴力地想,可以給它們還原一個考慮完所有權值小於\(W\)的邊之後的最小生成森林的聯通情況,也就是那個並查集的fat陣列,但是這顯然很蠢,因為很多值都是沒有用的。而我們需要的值就是加入邊\((u,v)\)

的時候用於判斷是否已經聯通的\(fat_u, fat_v\)。那麼我們可以首先生成一顆最小生成樹,和通常不同的是,對於一段邊權相同的邊,在考慮之前,先將\(fat_u, fat_v\)記錄下來,然後再全部一起加入。然後每次判斷的時候就模擬最小生成樹的操作,然後判斷完成的時候將所有\(fat_u, fat_v\)的值改回去。

#include <bits/stdc++.h>

using namespace std;

const int maxn = 5e5 + 5;
struct Edge {
    int u, v, fu, fv, id, w;
    friend bool operator < (Edge a, Edge b) {
        return a.w < b.w;
    }
} E[maxn];

int n, m, fat[maxn], ord[maxn], qls[maxn], qls_sz;
int getfa(int v) { return v == fat[v] ? v : fat[v] = getfa(fat[v]); }
bool cmp(int a, int b) { return ord[a] < ord[b]; }

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
        scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w), E[i].id = i;

    sort(E + 1, E + 1 + m);
    for (int i = 1; i <= n; i++) fat[i] = i;
    for (int st = 1, ed = 1; st <= m; st = ++ed) {
        //首先全部記下來
        while (E[ed].w == E[st].w && ed <= m) {
            E[ed].fu = getfa(E[ed].u), E[ed].fv = getfa(E[ed].v);
            ed++;
        }
        ed--;
        //然後加進最小生成森林中
        for (int i = st; i <= ed; i++) {
            int fu = getfa(E[i].u), fv = getfa(E[i].v);
            ord[E[i].id] = i;
            if (fu == fv) continue;
            fat[fu] = fv;
        }
    }

    int q; scanf("%d", &q);
    for (int i = 1; i <= n; i++) fat[i] = i;
    while (q--) {
        scanf("%d", &qls_sz);
        for (int i = 1; i <= qls_sz; i++) scanf("%d", &qls[i]);
        sort(qls + 1, qls + 1 + qls_sz, cmp);
        bool ans = true;
        for (int st = 1, ed = 1; st <= qls_sz; st = ++ed) {
            //模擬過程
            while (E[ord[qls[ed]]].w == E[ord[qls[st]]].w && ed <= qls_sz) {
                int p = ord[qls[ed]];
                int fu = getfa(E[p].fu), fv = getfa(E[p].fv);
                ed++;
                if (fu == fv) { ans = false; break; }
                fat[fu] = fv;
            }
            ed--;
            //改回去
            for (int i = st; i <= ed; i++) 
                fat[E[ord[qls[i]]].fu] = E[ord[qls[i]]].fu, 
                fat[E[ord[qls[i]]].fv] = E[ord[qls[i]]].fv;
        }
        if (ans) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}