严格次小生成树

前言

变态题。调了三四天才调出来。


如果最小生成树选择的边集是 \(E_M\)严格次小生成树选择的边集是 \(E_S\),那么需要满足:(\(\operatorname{value}(e)\) 表示边 \(e\) 的权值)\(\sum_{e\in E_M}\operatorname{value}(e)<\sum_{e\in E_S}\operatorname{value}(e)\)

注意:是严格小于!!!

大致思路如下:

  1. 先跑最小生成树(以 kruskal 为例),标记每一条加入最小生成树的边,算出最小生成树的边权和 \(sum\)
  2. 遍历所有不在最小生成树中的边 \(e\),将其加入。这时会出现一个环,找到环上的最大值 \(maxx\),则必有 \(maxx\le \operatorname{value}(e)\),故我们只需把 \(maxx\) 所对应的边扔掉,加入 \(e\),得到 \(res=sum-maxx+\operatorname{value}(e)\)
  3. \(ans=\max(res)\)

但有一个问题。

上面说了 \(maxx\le \operatorname{value}(e)\)

\(maxx=\operatorname{value}(e)\)!!!

这样就不严格了。

为了防止相等,我们需要多存一个次大值 \(minn\)(不是最小值)

找环上最大值可用 LCA(倍增),设要加入的边两端分别是 \(u\)\(v\),我们求出 \(lcaa=\operatorname{LCA}(u,v)\),然后查询 \(u\to lcaa\) 最大值和 \(v\to lcaa\) 最大值,两者取 \(\max\) 即可。查询思路类似 LCA 中让 \(x\)\(y\) 深度相等的部分,边跳边找。

int query(int u, int v, int w)
{
	int res = -INF;
	while (dep[u] > dep[v])
	{
		int h = lg[dep[u] - dep[v]];
		if (w != maxx[u][h])
		{
			res = max(res, maxx[u][h]);
		}
		else
		{
			res = max(res, minn[u][h]);
		}
		u = fa[u][h];
	}
	return res;
}

关键在于如何更新 \(maxx\)\(minn\)

\(\colorbox{lightgrey}{A}\;\text{---------}\;\colorbox{lightgrey}{B}\;\text{---------}\;\colorbox{lightgrey}{C}\)

这里 \(B\) 相当于 \(A\)\(2^{i-1}\) 级祖先;\(C\) 相当于 \(B\)\(2^{i-1}\) 级祖先,即 \(A\)\(2^i\) 级祖先。

显然 \(maxx(A)=\max(maxx(B),maxx(C))\)

对于 \(minn\),分情况讨论:

  1. \(maxx(B)=maxx(C)\):此时 \(minn(A)=\max(minn(B),minn(C))\)
  2. \(maxx(B)<maxx(C)\)\(minn(B)\) 肯定没希望了,\(minn(A)=\max(maxx(B),minn(C))\)
  3. \(maxx(B)>maxx(C)\):同理 \(minn(A)=\max(minn(B),maxx(C))\)
void work()
{
	for (int j = 1; j <= 18; j++)
	{
		for (int i = 1; i <= n; i++)
		{
			fa[i][j] = fa[fa[i][j - 1]][j - 1];
			maxx[i][j] = max(maxx[i][j - 1], maxx[fa[i][j - 1]][j - 1]);
			if (maxx[i][j - 1] == maxx[fa[i][j - 1]][j - 1])
			{
				minn[i][j] = max(minn[i][j - 1], minn[fa[i][j - 1]][j - 1]);
			}
   			else if (maxx[i][j - 1] < maxx[fa[i][j - 1]][j - 1])
   			{
   				minn[i][j] = max(maxx[i][j - 1], minn[fa[i][j - 1]][j - 1]);
			}
			else
			{
				minn[i][j] = max(minn[i][j - 1], maxx[fa[i][j - 1]][j - 1]);
			}
		}
	}
}

P4180 [BJWC2010]严格次小生成树

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;

const int MAXN = 1e5 + 5;
const int MAXM = 3e5 + 5;
const int MAXLOG = 20;
const int INF = 0x3f3f3f3f3f3f3f3fll;

int n, m, sum, ans = INF, max1, max2, cnt;
int head[MAXN], f[MAXN], fa[MAXN][MAXLOG], dep[MAXN], lg[MAXN], maxx[MAXN][MAXLOG], minn[MAXN][MAXLOG];

struct edge1
{
	int from, to, dis;
	bool vis;
	bool operator <(const edge1 &x)const
	{
		return x.dis > dis;
	}
}e1[MAXM];

struct edge2
{
	int to, dis, nxt;
}e2[MAXM << 1];

void add(int u, int v, int w)
{
	e2[++cnt] = edge2{v, w, head[u]};
	head[u] = cnt;
}

void init()
{
	for (int i = 1; i <= n; i++)
	{
		f[i] = i;
	}
}

int find(int x)
{
	if (x == f[x])
	{
		return x;
	}
	else
	{
		return f[x] = find(f[x]);
	}
}

void kruskal()
{
	sort(e1 + 1, e1 + m + 1);
	init();
	int tot = 0;
	for (int i = 1; i <= m; i++)
	{
		int u = e1[i].from, v = e1[i].to, w = e1[i].dis;
		int fu = find(u), fv = find(v);
		if (fu != fv)
		{
			f[fu] = fv;
			sum += w;
			e1[i].vis = true;
			add(u, v, w);
			add(v, u, w);
			if (++tot == n - 1)
			{
				break;
			}
		}
	}
}

void dfs(int u, int father)
{
	fa[u][0] = father;
	dep[u] = dep[father] + 1;
	minn[u][0] = -INF;
	for (int i = head[u]; i; i = e2[i].nxt)
	{
		int v = e2[i].to;
		if (v != father)
		{
			maxx[v][0] = e2[i].dis;
			dfs(v, u);
		}
	}
}

int lca(int x, int y)
{
	if (dep[x] < dep[y])
	{
		swap(x, y);
	}
	while (dep[x] > dep[y])
	{
		x = fa[x][lg[dep[x] - dep[y]]];
	}
	if (x == y)
	{
		return x;
	}
	for (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];
}

void work()
{
	for (int j = 1; j <= 18; j++)
	{
		for (int i = 1; i <= n; i++)
		{
			fa[i][j] = fa[fa[i][j - 1]][j - 1];
			maxx[i][j] = max(maxx[i][j - 1], maxx[fa[i][j - 1]][j - 1]);
			if (maxx[i][j - 1] == maxx[fa[i][j - 1]][j - 1])
			{
				minn[i][j] = max(minn[i][j - 1], minn[fa[i][j - 1]][j - 1]);
			}
   			else if (maxx[i][j - 1] < maxx[fa[i][j - 1]][j - 1])
   			{
   				minn[i][j] = max(maxx[i][j - 1], minn[fa[i][j - 1]][j - 1]);
			}
			else
			{
				minn[i][j] = max(minn[i][j - 1], maxx[fa[i][j - 1]][j - 1]);
			}
		}
	}
}

int query(int u, int v, int w)
{
	int res = -INF;
	while (dep[u] > dep[v])
	{
		int h = lg[dep[u] - dep[v]];
		if (w != maxx[u][h])
		{
			res = max(res, maxx[u][h]);
		}
		else
		{
			res = max(res, minn[u][h]);
		}
		u = fa[u][h];
	}
	return res;
}

signed main()
{
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		scanf("%lld%lld%lld", &e1[i].from, &e1[i].to, &e1[i].dis);
	}
	kruskal();
	for (int i = 2; i <= n; i++)
	{
		lg[i] = lg[i >> 1] + 1;
	}
	dfs(1, 0);
	work();
	for (int i = 1; i <= m; i++)
	{
		if (!e1[i].vis)
		{
			int u = e1[i].from, v = e1[i].to, w = e1[i].dis, lcaa = lca(u, v);
			int maxu = query(u, lcaa, w), maxv = query(v, lcaa, w);
			ans = min(ans, sum - max(maxu, maxv) + w);
		}
	}
	printf("%lld", ans);
	return 0;
}
posted @ 2021-08-07 18:04  mango09  阅读(60)  评论(0编辑  收藏  举报
-->