一名苦逼的OIer,想成为ACMer

Iowa_Battleship

BZOJ1977或洛谷4180 [BJWC2010]次小生成树

一道LCA+生成树

BZOJ原题链接

洛谷原题链接

细节挺多,我调了半天。。累炸。。

回到正题,我们先求出随便一棵最小生成树(设边权和为\(s\)),然后扫描剩下所有边,设扫到的边的两端点为\(x,y\),长度为\(z\),树上\(x,y\)间边权最大的边和严格次大的边分别为\(dis_1,dis_2\)
如果\(z>dis_1\),那么这条边可以替换掉\(dis_1\)对应的边,则得到一个可能答案\(s-dis_1+z\)
如果\(z=dis_1\),那么这条边可以替换掉\(dis_2\)对应的边,则得到一个可能答案\(s-dis_2+z\)
然后我们就可以用倍增\(LCA\)快速求树上\(x,y\)间边权最大的边和严格次大的边,定义数组\(g[x][k][0],g[x][k][1]\)表示从节点\(x\)往上跳\(2^k\)下所经过的路径的最大值和严格次小值,然后在预处理\(LCA\)的同时处理即可。
因为调到心态爆炸,所以代码可能比较丑。。

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int M = 3e5 + 10;
const int K = 17;
struct dd {
	int x, y, z;
};
dd a[M];
struct aw {
	int ma, se_ma;
	aw()
	{
		ma = se_ma = 0;
	}
};
int fi[N], di[N << 1], da[N << 1], ne[N << 1], f[N][K], g[N][K][2], de[N], fa[N], l, gn;
bool v[M];
int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c<'0' || c>'9'; c = getchar())
		p |= c == '-';
	for (; c >= '0'&&c <= '9'; c = getchar())
		x = x * 10 + (c - '0');
	return p ? -x : x;
}
int comp(dd x, dd y)
{
	return x.z < y.z;
}
inline void sw(int &x, int &y)
{
	int z = x;
	x = y;
	y = z;
}
inline int maxn(int x, int y)
{
	return x > y ? x : y;
}
inline ll minn(ll x, ll y)
{
	return x < y ? x : y;
}
inline int fin(int x)
{
	if (!(fa[x] ^ x))
		return x;
	return fa[x] = fin(fa[x]);
}
inline void add(int x, int y, int z)
{
	di[++l] = y;
	da[l] = z;
	ne[l] = fi[x];
	fi[x] = l;
}
void dfs(int x)
{
	int i, y;
	for (i = 1; i <= gn; i++)
	{
		f[x][i] = f[f[x][i - 1]][i - 1];
		g[x][i][0] = maxn(g[x][i - 1][0], g[f[x][i - 1]][i - 1][0]);
		g[x][i][1] = !(g[x][i - 1][0] ^ g[f[x][i - 1]][i - 1][0]) ? maxn(g[x][i - 1][1], g[f[x][i - 1]][i - 1][1]) : (g[x][i - 1][0] > g[f[x][i - 1]][i - 1][0]) ? maxn(g[x][i - 1][1], g[f[x][i - 1]][i - 1][0]) : maxn(g[x][i - 1][0], g[f[x][i - 1]][i - 1][1]);
	}
	for (i = fi[x]; i; i = ne[i])
	{
		y = di[i];
		if (!de[y])
		{
			de[y] = de[x] + 1;
			f[y][0] = x;
			g[y][0][0] = da[i];
			g[y][0][1] = -1e9;
			dfs(y);
		}
	}
}
aw fx(aw X, int i, int x)
{
	if (X.ma < g[x][i][0])
	{
		X.se_ma = maxn(X.ma, g[x][i][1]);
		X.ma = g[x][i][0];
	}
	else
		if (X.ma > g[x][i][0])
			X.se_ma = maxn(g[x][i][0], g[x][i][1]);
	return X;
}
aw lca(int x, int y)
{
	int i;
	aw X;
	if (de[x] > de[y])
		sw(x, y);
	for (i = gn; ~i; i--)
		if (de[f[y][i]] >= de[x])
		{
			X = fx(X, i, y);
			y = f[y][i];
		}
	if (!(x^y))
		return X;
	for (i = gn; ~i; i--)
		if (f[x][i] ^ f[y][i])
		{
			X = fx(X, i, x);
			X = fx(X, i, y);
			x = f[x][i];
			y = f[y][i];
		}
	X = fx(X, 0, x);
	return X;
}
int main()
{
	int i, k = 0, n, m, x, y;
	ll s = 0, mi = 1e18;
	n = re();
	m = re();
	gn = log2(n);
	for (i = 1; i <= n; i++)
		fa[i] = i;
	for (i = 1; i <= m; i++)
	{
		a[i].x = re();
		a[i].y = re();
		a[i].z = re();
	}
	sort(a + 1, a + m + 1, comp);
	for (i = 1; i <= m; i++)
	{
		x = fin(a[i].x);
		y = fin(a[i].y);
		if (x^y)
		{
			fa[y] = x;
			k++;
			s += a[i].z;
			add(a[i].x, a[i].y, a[i].z);
			add(a[i].y, a[i].x, a[i].z);
			v[i] = 1;
		}
		if (!(k ^ (n - 1)))
			break;
	}
	de[1] = 1;
	dfs(1);
	aw X;
	for (i = 1; i <= m; i++)
		if (!v[i])
		{
			X = lca(a[i].x, a[i].y);
			if (!(a[i].z^X.ma))
				mi = minn(mi, s - X.se_ma + a[i].z);
			else
				mi = minn(mi, s - X.ma + a[i].z);
		}
	printf("%lld", mi);
	return 0;
}

posted on 2018-09-03 14:53  Iowa_Battleship  阅读(259)  评论(0编辑  收藏  举报

导航