題意
給出一張聯通圖,圖上每個點有紅藍兩色,給每一條邊都賦一個權值,使得所有的紅邊構成一顆最小生成樹,且權值是 \(1\) 到 \(m\) 的排列,求字典序最小的情況。
題解
對於一條藍邊,其權值一定小於除它以外全是紅邊的環上的所有紅邊的權值(有點繞),否則就構不成一顆生成樹。
所以只需要按順序列舉藍邊,然後按字典序染環上的紅邊,最後染這條藍邊即可。
namespace zqh {
const int N = 500005;
int n, m;
vector<pair<int, int>> g[N];
struct edge {
int id, v;
bool w;
} e[N];
int fa[N], dep[N], top[N], val[N], fid[N], k;
void dfs(int id, int f, int dp) {
fa[id] = f;
dep[id] = dp;
for (pii x : g[id]) {
if (f == x.first) continue;
fid[x.first] = x.second;
dfs(x.first, id, dp + 1);
}
}
void add(int id) {
if (e[id].w) {
if (!val[id]) {
val[id] = ++k;
// cout << id << " t1o " << k << endl;
}
return;
}
int x = e[id].id, y = e[id].v, u = x, v = y;
vector<int> tmp;
while (top[u] != top[v]) {
// cout << u << " " << v << endl;
// cout << top[u] << " " << top[v] << endl;
// cout << fa[u] << " " << fa[v] << endl;
if (dep[top[u]] > dep[top[v]]) {
if (!val[fid[top[u]]]) tmp.push_back(fid[top[u]]);
u = fa[top[u]];
} else {
if (!val[fid[top[v]]]) tmp.push_back(fid[top[v]]);
v = fa[top[v]];
}
}
sort(tmp.begin(), tmp.end());
for (int x : tmp) {
val[x] = ++k;
// cout << x << " t2o " << k << endl;
}
val[id] = ++k;
// cout << id << " t3o " << k << endl;
int p = u, q = v;
u = x, v = y;
while (top[u] != top[v]) {
if (dep[top[u]] > dep[top[v]]) {
int tmp2 = u;
u = fa[top[u]];
top[tmp2] = top[p];
} else {
int tmp2 = v;
v = fa[top[v]];
top[tmp2] = top[q];
}
}
}
void init() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int id, v;
bool w;
cin >> id >> v >> w;
e[i] = {id, v, w};
if (!w) continue;
g[id].push_back({v, i});
g[v].push_back({id, i});
}
dfs(e[1].id, 0, 1);
}
void solve() {
for (int i = 1; i <= n; i++) {
top[i] = i;
}
for (int i = 1; i <= m; i++) {
add(i);
}
for (int i = 1; i <= m; i++) {
cout << val[i] << " ";
}
}
void main() {
init();
solve();
}
}