P4180 [BJWC2010]严格次小生成树

96 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

传送门 如题,严格次小生成树

分析

对于 KruskalKruskal 执行过程中 先构造最小生成树,然后再贪心的删边 剩下没有参与构造生成树的边,对于边的两个端点 用这条边(记为AA边),替换生成树两个端点路径上面的一条边(记为BB边) 严格次小,AA 边一定严格 大于 BB 的 我们要求的是对于每条能够替换的边,v(A)v(B)v(A)-v(B)最小即可

倍增维护生成树链上边的最大值和次大值,按照上面的规则尝试替换边

代码

//P4180
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;

int head[MAX_N];
int tot = 0;
struct Edge {
	int to, nxt, w;
}edge[MAX_N];

void addEdge(int u, int v, int w) {
	edge[tot].nxt = head[u];
	edge[tot].to = v;
	edge[tot].w = w;
	head[u] = tot++;
	edge[tot].nxt = head[v];
	edge[tot].to = u;
	edge[tot].w = w;
	head[v] = tot++;
}

bool use[MAX_N];
struct Node {
	int x, y, w;
}node[MAX_N];

int father[MAX_N];
int find(int x) {
	return x == father[x] ? x : father[x] = find(father[x]);
}

int dep[MAX_N];
int parent[MAX_N][22];
int maxx[MAX_N][22];
int maxx2[MAX_N][22];

void dfs(int u, int from) {
	dep[u] = dep[from] + 1;
	parent[u][0] = from;
	for (int i = 1; i <= 20; ++i) {
		parent[u][i] = parent[parent[u][i-1]][i-1];
		maxx[u][i] = max(maxx[parent[u][i-1]][i-1], maxx[u][i-1]);
		maxx2[u][i] = max(maxx2[parent[u][i-1]][i-1], maxx2[u][i-1]);
		if (maxx[parent[u][i-1]][i-1] != maxx[u][i-1]) {
			maxx2[u][i] = max(maxx2[u][i], min(maxx[parent[u][i-1]][i-1], maxx[u][i-1]));
		}
	}
	
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from) continue;
		maxx[v][0] = edge[i].w;
		maxx2[v][0] = 0;
		dfs(v, u);
	}
}

int LCA(int x, int y) {
	if (dep[x] < dep[y]) swap(x, y);
	
	for (int i = 20; i+1; --i) {
		if (dep[parent[x][i]] >= dep[y]) {
			x = parent[x][i];
		}
	}
	
	if (x == y) return x;
	
	for (int i = 20; i+1; --i) {
		if (parent[x][i] != parent[y][i]) {
			x = parent[x][i];
			y = parent[y][i];
		}
	}
	return parent[x][0];
}

int get_max(int u, int lca, int limit) {
	int res = 0;
	for (int i = 20; i+1; --i) {
		if (dep[parent[u][i]] >= dep[lca]) {
			if (maxx[u][i] < limit) {
				res = max(res, maxx[u][i]);
			}
			if (maxx2[u][i] < limit) {
				res = max(res, maxx2[u][i]);
			}
			u = parent[u][i];
		}
	}
	return res;
}

void init() {
	memset(head, -1, sizeof head);
	tot = 0;
}

void solve(){
	init();
	cin >> N >> M;
	
	int u, v, w;
	for (int i = 1; i <= M; ++i) {
		cin >> u >> v >> w;
		node[i] = {u, v, w};
	}
	
	sort(node+1, node+1+M, [](Node a, Node b){return a.w < b.w;});
	
	for (int i = 1; i <= N; ++i) {
		father[i] = i;
	}
	
	int done = 1;
	int x, y;
	int ans = 0;
	for (int i = 1; i <= M && done < N; ++i) {
		x = find(node[i].x);
		y = find(node[i].y);
		if (x == y) continue;
		use[i] = 1;
		ans += node[i].w;
		++done;
		father[x] = y;
		addEdge(node[i].x, node[i].y, node[i].w);
	}
	
	dfs(1, 0);
	
	int res = 1e18;
	for (int i = 1; i <= M; ++i) {
		if (use[i]) continue;
		x = node[i].x;
		y = node[i].y;
		
		int lca = LCA(x, y);
		int a = get_max(x, lca, node[i].w);
		int b = get_max(y, lca, node[i].w);
		res = min(res, ans - max(a, b) + node[i].w);
	}
	cout << res;
}

signed main()
{
	#ifndef ONLINE_JUDGE
	//FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}