TIOJ 2269:計算機結構
TIOJ 2269:計算機結構
題目大意:有一張 $n\leq 2000$ 個點 $m\leq 10^6$ 條邊的無向圖,有 $k\leq 2000$ 種顏色,每條邊有其中一個想要塗的顏色。一開始每條邊都沒有顏色,每次可以選一個簡單環塗一個顏色,若有邊已經有顏色會被蓋過去,問能不能塗成想要的顏色。
解法:倒過來做,維護 $k$ 種顏色邊的生成森林,每次加邊會有兩種情況,第一種是形成一個環,那要把環上的邊都加進 queue,第二種是把兩棵樹接起來,那可以啟發式合併,樹上已經加過的邊可以用並查集跳過,在 queue 裡的邊可以想成變沒有顏色了,所以要把 $k$ 個森林都看過一遍,另外維護一個 dsu,如果一條邊的兩個點 $a,b$ 已經連通,那沒有必要加,因為已經有一條 $a$ 到 $b$ 的路徑被加進去了,時間複雜度 $O(nk\log n+m)$。
$\text{Code:}$
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,popcnt,sse4,abm")
#include <bits/stdc++.h>
using namespace std;
const int N = 2005;
const int M = 1e6 + 5;
int n, m, k;
bitset<M> vs;
queue<tuple<int, int, int>> q;
struct DSU {
vector<int> to, sz;
void init() {
to.resize(n + 1);
sz.resize(n + 1);
iota(to.begin() + 1, to.end(), 1);
fill(sz.begin() + 1, sz.end(), 1);
}
int dsu(int x) {
return x == to[x] ? x : (to[x] = dsu(to[x]));
}
inline bool merge(int a, int b) {
a = dsu(a), b = dsu(b);
if (a == b) return 0;
if (sz[a] < sz[b]) swap(a, b);
to[b] = a;
sz[a] += sz[b];
return 1;
}
} Dsu;
struct DSU2 {
vector<int> to;
void init() {
to.resize(n + 1);
iota(to.begin() + 1, to.end(), 1);
}
int dsu(int x) {
return x == to[x] ? x : (to[x] = dsu(to[x]));
}
inline void merge(int a, int b) {
a = dsu(a), b = dsu(b);
if (a == b) return;
to[b] = a;
}
};
struct Tree {
vector<tuple<int, int, int>> e;
vector<vector<pair<int, int>>> adj;
vector<int> pa, pid, h;
DSU dsu;
DSU2 dsu2;
void init() {
e.clear();
adj = vector<vector<pair<int, int>>>(n + 1, vector<pair<int, int>>());
dsu.init();
dsu2.init();
pa = pid = h = vector<int>(n + 1, 0);
}
void dfs(int pos) {
for (auto &[np, id] : adj[pos]) if (np != pa[pos]) {
h[np] = h[pos] + 1;
pa[np] = pos;
pid[np] = id;
dsu2.to[np] = (vs[id] ? dsu2.to[pos] : np);
dfs(np);
}
}
void build() {
for (auto &[a, b, i] : e) {
if (dsu.merge(a, b)) {
adj[a].emplace_back(b, i);
adj[b].emplace_back(a, i);
} else {
vs[i] = 1;
q.emplace(a, b, i);
}
}
for (int i = 1; i <= n; i++) if (h[i] == 0) {
h[i] = 1;
dfs(i);
}
}
inline void add(int a, int b, int id) {
if (dsu.dsu(a) != dsu.dsu(b)) {
if (dsu.sz[dsu.dsu(a)] < dsu.sz[dsu.dsu(b)]) swap(a, b);
dsu.merge(a, b);
adj[a].emplace_back(b, id);
adj[b].emplace_back(a, id);
h[b] = h[a] + 1;
pa[b] = a;
pid[b] = id;
dsu2.to[b] = a;
dfs(b);
return;
}
a = dsu2.dsu(a);
b = dsu2.dsu(b);
while (a != b) {
if (h[a] < h[b]) swap(a, b);
if (vs[pid[a]] == 0) {
vs[pid[a]] = 1;
q.emplace(a, pa[a], pid[a]);
}
dsu2.merge(pa[a], a);
a = dsu2.dsu(a);
}
}
} tree[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
cin >> n >> m >> k;
for (int i = 1; i <= k; i++) tree[i].init();
for (int i = 1; i <= m; i++) {
int a, b, c;
cin >> a >> b >> c;
vs[i] = 0;
tree[c].e.emplace_back(a, b, i);
}
for (int i = 1; i <= k; i++) tree[i].build();
Dsu.init();
int cnt = 0;
while (q.size()) {
auto [a, b, id] = q.front();
q.pop();
cnt++;
if (Dsu.merge(a, b) == 0) continue;
for (int i = 1; i <= k; i++) tree[i].add(a, b, id);
}
cout << (m == cnt ? "Yes\n" : "No\n");
}
}
我的分享就到這裡結束了,如果喜歡我的 $\text{Blog}$,歡迎追蹤!
留言
張貼留言