码迷,mamicode.com
首页 > 其他好文 > 详细

[BJWC2010] 严格次小生成树

时间:2021-06-02 16:03:33      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:最大和   生成树   sort   wap   tor   flag   最大值   生成   struct   

题面

严格次小生成树

题解

小蓝书 + 我自己的补充

做法

题意很好理解吧。
设最小生成树的边权之和为 \(sum\)
我们要找严格次小生成树,就是要找到这样的一条非最小生成树上的边,满足:

  • 将最小生成树上的某条边替换成这条边后,树依然联通。
  • 这条边与被替换边的权值之差最小,且大于 \(0\)

所以我们进行如下操作:

  • 选择一条非最小生成树上的边 \((x,y,z)\)
  • 将它加入树中,显然会形成一个环。
  • \(Kruskal\) 的证明过程我们可以得到,\(z \geq dis_{x,y}\)
  • 所以我们可以将 \(x\) - \(y\) 路径上的某条边替换成边 \((x,y,z)\) ,显然树依然联通。
  • \(x\) - \(y\) 路径上的权值最大边的边权为 \(val_1\) ,次大边的边权为 \(val_2\)
  • 根据上述严格次小生成树的找法的定义, 当 \(z > val_1\) 时 ,将 \(val_1\) 的这条边替换成 \((x,y,z)\) 肯定是最优的,得到候选答案 \(sum - val_1 + z\)。当 \(z = val_1\) 时,将 \(val_2\) 替换成 \((x,y,z)\) 肯定是最优的,因为 \(val_1\) 所在的边不能被替换,得到候选答案 \(sum - val_2 + z\)
  • 对所有的非树边执行上述操作,记录最小的范围值,得到最终答案。

优化

显然每次暴查 \(val_1,val_2\) 明显会炸。
所以我们可以运用倍增的思想预处理出点 \(x\) 向上跳 \(2^k\) 次的路径中的 \(val_1\)\(val_2\)
做法类似与 \(lca\) 倍增做法时维护祖先的做法,这到题我们在找 \(x\) - \(y\) 的路径时也需要 \(lca\) ,所以这两个我们也需要维护出来。
\(g[x][k][0/1]\) 表示点 \(x\),向上跳 \(2^k\) 次的路径中的 \(val_1\)\(val_2\)

  • 初始化:

\[g[x][0][0] = w_{x,fa_x},g[x][0][1] = -INF(设为负无穷不为影响到其它值的维护) \]

  • \(Fa 表示 x的2^{k - 1}辈祖先\)

\[g[x][k][0] = max(g[x][k - 1][0],g[][k - 1][0] ) \]

\[g[x][k][1] = \left\{ \begin{array}{lcl} max(g[x][k - 1][1],g[Fa][k - 1][1])\ (g[x][k - 1][0] = g[Fa][k -1][0]) \max(g[x][k - 1][0],g[Fa][k - 1][1])\ (g[x][k - 1][0] < g[Fa][k -1][0]) \max(g[x][k - 1][1],g[Fa][k - 1][0])\ (g[x][k - 1][0] > g[Fa][k -1][0]) \\end{array} \right\}\]

转移过程应该很好想,最大值相等从次小值里找最小值,一个更大,从小的最大和大的次小里找次小值。
注意要开 \(long\ long\)

代码

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<iostream>
#define LL long long

using namespace std;

const int N = 1e5 + 5;
const int M = 3e5 + 5;
const LL INF = 1e16;

int n,m,f[N][18],t;
LL ans = 0,g[N][18][2],res = INF;
struct E {
	int from,to,w; bool is;
	E () {
		is = false;
	}
	bool operator < (const E & x) const {
		return w < x.w;
	}
}e[M];

struct edge {
	int head[N],next[M],to[M],w[M],size;
	inline void add(int u,int v,int W) {
		next[++size] = head[u]; w[size] = W;
		to[size] = v; head[u] = size;
		next[++size] = head[v]; w[size] = W;
		to[size] = u; head[v] = size;
	} 
	inline LL Get_val_2(int y,int j) {
		int Fa = f[y][j - 1]; LL Ans;
		if(g[y][j - 1][0] == g[Fa][j - 1][0])
			Ans = max(g[y][j - 1][1],g[Fa][j - 1][1]);	
		else if(g[y][j - 1][0] < g[Fa][j - 1][0])
			Ans = max(g[y][j - 1][0],g[Fa][j - 1][1]);
		else Ans = max(g[y][j - 1][1],g[Fa][j - 1][0]);
		return Ans;
	}
	queue<int> q; int dep[N]; LL dis[N];
	void bfs(int s) {
		q.push(s); dep[s] = 1;
		while(!q.empty()) {
			int x = q.front(); q.pop();
			for(int i = head[x]; i; i = next[i]) {
				int y = to[i];
				if(dep[y]) continue;
				dep[y] = dep[x] + 1;
				dis[y] = dis[x] + w[i];
				f[y][0] = x;
				g[y][0][0] = w[i]; g[y][0][1] = -INF;
				for(int j = 1; j <= t; j++) {
					f[y][j] = f[f[y][j - 1]][j - 1];
					g[y][j][0] = max(g[y][j - 1][0],g[f[y][j - 1]][j - 1][0]);
					g[y][j][1] = max(g[y][j][1],Get_val_2(y,j));
				}
				q.push(y);
			}	
		}
	} 
	inline LL Get(int x,int y,int w) {
		if(dep[x] > dep[y]) swap(x,y);
		LL val_1 = 0,val_2 = 0;
		for(int i = t; i >= 0; i--)
			if(dep[f[y][i]] >= dep[x]) {
				val_1 = max(val_1,g[y][i][0]);
				if(i > 0) val_2 = max(val_2,Get_val_2(y,i));
				y = f[y][i];
			}
		if(x == y) {
			if(w >  val_1) return ans - val_1 + w;
			if(w == val_1) return ans - val_2 + w; 
		}
		for(int i = t; i >= 0; i--)
			if(f[x][i] != f[y][i]) {
				val_1 = max(val_1,g[y][i][0]);
				if(i > 0) val_2 = max(val_2,Get_val_2(y,i));
				val_1 = max(val_1,g[x][i][0]);
				if(i > 0) val_2 = max(val_2,Get_val_2(x,i));
				x = f[x][i],y = f[y][i];
			}
		val_1 = max(val_1,g[x][0][0]);
		val_1 = max(val_1,g[y][0][0]);
		val_2 = max(val_2,max(g[x][0][0],g[y][0][0]));
		if(w >  val_1) return ans - val_1 + w;
		if(w == val_1) return ans - val_2 + w; 
	}
}a;

int fa[N];
inline int Find(int x) {
	return fa[x] == x ? x : fa[x] = Find(fa[x]);
}

inline int read() {
	int x = 0,flag = 1;
	char ch = getchar();
	while(ch < ‘0‘ || ch > ‘9‘){if(ch == ‘-‘)flag = -1;ch = getchar();}
	while(ch >=‘0‘ && ch <=‘9‘){x = (x << 3) + (x << 1) + ch - 48;ch = getchar();}
	return x * flag;
}
int main() {
	n = read(),m = read();
	t = log(1.0 * n) / log(2.0);
	for(int i = 1; i <= m; i++) {
		e[i].from = read();
		e[i].to = read();
		e[i].w = read();
	}
	for(int i = 1; i <= n; i++) fa[i] = i;
	sort(e + 1,e + 1 + m); a.size = 1;
	for(int i = 1; i <= m; i++) {
		int x = Find(e[i].from);
		int y = Find(e[i].to);
		if(x == y) continue;
		fa[x] = y; ans += e[i].w;
		a.add(e[i].from,e[i].to,e[i].w); e[i].is = true;
	}
	a.bfs(1);
	for(int i = 1; i <= m; i++) {
		if(e[i].is) continue;
		res = min(res,a.Get(e[i].from,e[i].to,e[i].w));
	}
	printf("%lld\n",res);
	return 0;
}

[BJWC2010] 严格次小生成树

标签:最大和   生成树   sort   wap   tor   flag   最大值   生成   struct   

原文地址:https://www.cnblogs.com/sjzyh/p/14826151.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!