洛谷 P4180

题目链接:P4180 [BJWC2010]严格次小生成树

题目大意

就是求严格次小生成树, 即找到一个生成树, 使其子树和大于最小生成树,且最小

solution

那我们怎么求呢?

我们先建出最小生成树肯定不亏,然后我们根据最小生成树的性质,每加入一条边会发现构成了一个环,然后呢我们在这个环中找到最大值和次大值,然后如果最大值和个加入的边不相等就删去最大值,加入这条边,反之删去最小值,加入这条边,然后最大值和最小值可以用倍增来维护

Code:

/**
*    Author: Alieme
*    Data: 2020.8.31
*    Problem: P4180
*    Time: O()
*/
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>

#define int long long
#define rr register

#define inf 1e15
#define MAXN 300010

using namespace std;

inline int read() {
	int s = 0, f = 0;
	char ch = getchar();
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void print(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) print(x / 10);
	putchar(x % 10 + 48);
}

struct Ufs {
	int fa[MAXN];
	inline void clear(int x) {for (rr int i = 1; i <= x; i++) fa[i] = i;}
	int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
	inline void merge(int x, int y) {x = find(x), y = find(y); if (x != y) fa[x] = y;}
	inline bool check(int x, int y) {return find(x) == find(y);}
}S;

struct Gragh {
	int u, v, w;
	Gragh() {}
	Gragh(int U, int V, int W) {u = U, v = V, w = W;}
	bool operator <(const Gragh &b) const {return w < b.w;}
}g[MAXN];

struct Edge {
	int nxt;
	int to;
	int val;
	Edge() {}
	Edge(int Nxt, int To, int Val) {nxt = Nxt, to = To, val = Val;}
}e[MAXN << 1];

int n, m, tot, ans = inf, cnt;

int head[MAXN << 1], dep[MAXN];

int fa[MAXN][30], minn[MAXN][30], maxx[MAXN][30];

bool vis[MAXN];

inline void add(int from, int to, int val) {
	e[++tot] = Edge(head[from], to, val);
	head[from] = tot;
}

void dfs(int x, int fath) {
	fa[x][0] = fath;
	dep[x] = dep[fath] + 1;
	for (rr int i = head[x]; i; i = e[i].nxt) {
		int to = e[i].to;
		if (to == fath) continue;
		maxx[to][0] = e[i].val;
		minn[to][0] = -inf;
		dfs(to, x);
	}
}

inline void init() {
	minn[1][0] = inf;
	dfs(1, 0);
	for (rr int i = 1; i <= 18; i++)
		for (rr int j = 1; j <= n; j++) {
			fa[j][i] = fa[fa[j][i - 1]][i - 1];
			maxx[j][i] = max(maxx[j][i - 1], maxx[fa[j][i - 1]][i - 1]);
			minn[j][i] = min(minn[j][i - 1], minn[fa[j][i - 1]][i - 1]);
			if (maxx[j][i - 1] > maxx[fa[j][i - 1]][i - 1]) minn[j][i] = max(minn[j][i], maxx[fa[j][i - 1]][i - 1]);
			else if (maxx[j][i - 1] < maxx[fa[j][i - 1]][i - 1]) minn[j][i] = max(minn[j][i], maxx[j][i - 1]);
		}
}

inline void kruskal() {
	S.clear(n);
	sort(g + 1, g + 1 + m);
	for (rr int i = 1; i <= m; i++) {
		int u = g[i].u, v = g[i].v, w = g[i].w;
		if (S.check(u, v)) continue;
		vis[i] = 1;
		cnt += w;
		S.merge(u, v);
		add(u, v, w);
		add(v, u, w);
	}
}

inline int LCA(int x, int y) {
	if (dep[x] < dep[y]) swap(x, y);
	for (rr int i = 18; i >= 0; i--)
		if (dep[fa[x][i]] >= dep[y])
			x = fa[x][i];
	if (x == y) return x;
	for (rr int i = 18; i >= 0; i--) 
		if (fa[x][i]^fa[y][i])
			x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}

inline int query(int u, int v, int maxxx) {
	int ans = -inf;
	for (int i = 18; i >= 0; i--) 
		if (dep[fa[u][i]] >= dep[v]) {
			if (maxxx != maxx[u][i]) ans = max(ans, maxx[u][i]);
			else ans = max(ans, minn[u][i]);
			// cout << ans << " " << maxx[u][i] << " " << minn[u][i] << "\n"; 
			u = fa[u][i];
		}
	return ans;
}

signed main() {
	n = read();
	m = read();
	for (rr int i = 1; i <= m; i++) g[i].u = read(), g[i].v = read(), g[i].w = read();
	kruskal();
	init();
	for (rr int i = 1; i <= m; i++) 
		if (!vis[i]) {
			int u = g[i].u, v = g[i].v, w = g[i].w;
			int lca = LCA(u, v);
			int maxu = query(u, lca, w);
			int maxv = query(v, lca, w);
			ans = min(ans, cnt - max(maxu, maxv) + w);
		}
	print(ans);
}
上一篇:洛谷 P1073 最优贸易(DFS+DP)


下一篇:C#枚举高级战术