[題解][CF-891C]Envy
阿新 • • 發佈:2021-12-16
有的\(\mathrm{dalao}\)說這道題可以用虛樹寫,但是本人想不到。
考慮使用\(\mathrm{Kruskal}\)建立最小生成樹的過程,可以發現一個性質:當我們考慮了所有邊權\(\leq w\)的邊,無論我們使用的是哪些邊,構成的最小生成森林的連通性一定是一樣的。
也就是說,對於一個詢問中的權值不同的邊,可以分開判斷。
考慮判斷一些權值相同(假設為\(W\))的邊是否可以放到同一顆生成樹中。暴力地想,可以給它們還原一個考慮完所有權值小於\(W\)的邊之後的最小生成森林的聯通情況,也就是那個並查集的fat
陣列,但是這顯然很蠢,因為很多值都是沒有用的。而我們需要的值就是加入邊\((u,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; }