P4180 【模板】严格次小生成树[BJWC2010]

P4180 【模板】严格次小生成树[BJWC2010]

题目描述
小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) (sum_{e in E_M}value(e)<sum_{e in E_S}value(e))
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

输入输出格式
输入格式:
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

输出格式:
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)


首先明确严格, 指的是次小生成树边权之和小于而不是小于等于最小生成树
搞清楚这点, 我们就有了一个大概思路: 先构造一个最小生成树, 枚举每一条没选上的边, 查询这条边连接的两点之间在最小生成树上的最大值和次大值(次大值防止出现等于的情况, 严格), 进行替换, 维护答案最小即可。

首先想到树剖, 发现线段树维护次大值较为繁琐且查询复杂度不够理想, 常熟较大。

考虑倍增 (LCA) ,在倍增初始化时预处理出一段链上(具体是从当前点向上跳 (2^{i}) 个点的这条链上)的最大、次大值。因为树上两点间有且仅有一条路径且此路径通过他们的 (LCA) ,故查询最大、 次大值时, 分别倍增查询两个节点到其 (LCA) 的最大值即可

查询过程中注意严格这一定义

复杂度:(O(Kruskal) + O(nlog n))

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
typedef long long LL;
using namespace std;
LL RD(){
    LL out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const LL maxn = 300019,INF = 0xfffffffffffffff;
LL head[maxn],nume = 1;
struct Node{
    LL v,dis,nxt;
    }E[maxn << 3];
void add(LL u,LL v,LL dis){
    E[++nume].nxt = head[u];
    E[nume].v = v;
    E[nume].dis = dis;
    head[u] = nume;
    }
LL num, nr;
LL father[maxn];
LL findfather(LL v){
	if(father[v] == v)return v;
	return father[v] = findfather(father[v]);
	}
struct Edge{
	LL u, v, dis;
	bool used;
	}I[maxn];
bool cmp(Edge a, Edge b){return a.dis < b.dis;}
LL tot, root;
LL jump[maxn][25],dep[maxn];
LL maxx[maxn][25], mx[maxn][25];
void dfs(LL u, LL F){
	for(LL i = head[u];i;i = E[i].nxt){
		LL v = E[i].v;
		if(v == F)continue;
		jump[v][0] = u;
		dep[v] = dep[u] + 1;
		maxx[v][0] = E[i].dis;
		mx[v][0] = -INF;
		dfs(v, u);
		}
	}
void get_jump(LL s){
	dep[s] = 1;
	dfs(s, -1);
	for(LL i = 1;i <= 19;i++){
		for(LL j = 1;j <= num;j++){
			LL F = jump[j][i - 1];
			jump[j][i] = jump[F][i - 1];
			maxx[j][i] = max(maxx[j][i - 1], maxx[F][i - 1]);
			mx[j][i] = max(mx[j][i - 1], mx[F][i - 1]);
			if(maxx[j][i] != maxx[j][i - 1])mx[j][i] = max(mx[j][i], maxx[j][i - 1]);
			else mx[j][i] = max(mx[j][i], maxx[F][i - 1]);
			}
		}
	}
LL LCA(LL x, LL y){
	if(dep[x] < dep[y])swap(x, y);
	for(LL i = 19;i >= 0;i--)if(dep[jump[x][i]] >= dep[y])x = jump[x][i];
	if(x == y)return x;
	for(LL i = 19;i >= 0;i--)if(jump[x][i] != jump[y][i])x = jump[x][i], y = jump[y][i];
	return jump[x][0];
	}
LL get_max(LL x, LL y, LL MAX){
	LL lca = LCA(x, y);
	LL ans = -INF;
	for(LL i = 19;i >= 0;i--){
		if(dep[jump[x][i]] >= dep[lca]){
			if(maxx[x][i] < MAX)ans = max(ans, maxx[x][i]);
			else ans = max(ans, mx[x][i]);
			x = jump[x][i];
			}
		}
	for(LL i = 19;i >= 0;i--){
		if(dep[jump[y][i]] >= dep[lca]){
			if(maxx[y][i] < MAX)ans = max(ans, maxx[y][i]);
			else ans = max(ans, mx[y][i]);
			y = jump[y][i];
			}
		}
	return ans;
	}
int main(){
	num = RD();nr = RD();
	for(LL i = 1;i <= num;i++)father[i] = i;
	for(LL i = 1;i <= nr;i++)I[i].u = RD(), I[i].v = RD(), I[i].dis = RD();
	sort(I + 1, I + 1 + nr, cmp);
	for(LL i = 1;i <= nr;i++){
		LL faA = findfather(I[i].u), faB = findfather(I[i].v);
		if(faA != faB){
			root = I[i].u;
			father[faA] = faB, tot += I[i].dis;
			add(I[i].u, I[i].v, I[i].dis);
			add(I[i].v, I[i].u, I[i].dis);
			I[i].used = 1;
			}
		}
	get_jump(root);
	LL ans = INF;
	for(LL i = 1;i <= nr;i++){
		if(!I[i].used){
			LL now = get_max(I[i].u, I[i].v, I[i].dis);
			ans = min(ans, tot - now + I[i].dis);
			}
		}
	printf("%lld
", ans);
	return 0;
	}
原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9402271.html