數列 題解

_zqh發表於2024-10-05

題意

給出一張聯通圖,圖上每個點有紅藍兩色,給每一條邊都賦一個權值,使得所有的紅邊構成一顆最小生成樹,且權值是 \(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();
	}
}

相關文章