其他分享
首页 > 其他分享> > [BJWC2010]严格次小生成树

[BJWC2010]严格次小生成树

作者:互联网

做题时间:2021.8.8

\(【题目描述】\)

给你一张 \(N(1\leq N\leq 10^5)\) 个点 \(M(1\leq M\leq 3\times 10^5)\) 条边的无向图,求出这张图中严格次小生成树。

\(【输入格式】\)

第一行两个整数 \(N,M\)
接下来 \(M\) 行每行三个整数 \(u,v,w\) 表示图上一条边

\(【输出格式】\)

一行一个数表示答案

\(【考点】\)

最小生成树、倍增DP

\(【做法】\)

次小生成树与最小生成树有关,可以考虑先求出最小生成树,然后遍历不在最小生成树上的每一条边,加入树中,并删除所形成的环中的一条边使其重新构成一棵新树,删除的这条边必定是环中最大或次大的(当最大值=加入的边的边权时,应当删除次大边),因此问题转化为:求最小生成树+枚举边 \((u,v)\) + 倍增求 \((u,lca)\) 和 \((v,lca)\) 路径上的最大值和次大值。

最后一步可以像求LCA一样分成三个部分:

  1. 求出 \(m_{u,i}\) 与 \(s_{u,i}\) 分别表示 \(u\) 到其 \(2^i\) 级祖先的路径上的最大值以及次大值。具体而言,定义 \(k\) 表示 \(u\) 的 \(2^{i-1}\) 级祖先,若 \(m_{u,i-1}>m_{k,i-1}\) ,则有 \(m_{u,i}=\max(m_{k,i-1},s_{u,i-1})\) ,若 \(m_{u,i-1}<m_{k,i-1}\) ,则有 \(m_{u,i}=\max(m_{u,i-1},s_{k,i-1})\) ,若 \(m_{u,i-1}=m_{k,i-1}\) ,则有 \(m_{u,i}=\max(s_{u,i-1},s_{k,i-1})\)

  2. 将 \(u,v\) 中深度较大的先上跳至与 \(v\) 深度相同处(设它是 \(u\) ),上跳过程中求最大值与次大值与1同理。

  3. 将 \(u,v\) 一起上跳至lca的儿子处。

注意:输入数据中可能会有自环,影响答案,要进行判断;

#include<cstdio>
#include<iomanip>
#include<algorithm>
using namespace std;
const int N=1e5+50,M=3e5+50;
typedef long long ll;
struct edge{
	int to,nxt,val;
}a[N<<1];
int head[N],tot;
struct G{
	int u,v,w;
}g[M];
int fa[N],f[N][30],dep[N];
int maxn[N][30],Sec_maxn[N][30];
bool vis[M];
int n,m;
inline ll Min(ll a,ll b){return a<b?a:b;}
inline ll Max(ll a,ll b){return a>b?a:b;}
inline ll Swap(int &x,int &y){int t=x;x=y;y=t;}

#define m1 maxn[x][i-1]
#define m2 maxn[f[x][i-1]][i-1]
#define m3 maxn[x][i]

#define s1 Sec_maxn[x][i-1]
#define s2 Sec_maxn[f[x][i-1]][i-1]
#define s3 Sec_maxn[x][i]


void Work1(int x,int fa,int pre)
{
	dep[x]=dep[fa]+1,f[x][0]=fa;
	maxn[x][0]=pre;
	for(int i=1;i<=20;i++){
		f[x][i]=f[f[x][i-1]][i-1];
		m3=Max(m1,m2);
		
		//分三种情况 
		if(m1>m2) s3=Max(m2,s1);//m1>m2>s2 ,只有m2和s1有机会成为次大值 
		if(m1==m2) s3=Max(s1,s2);//m1==m2>s1,s2 ,只有m2和s1有机会成为次大值 
		if(m1<m2) s3=Max(m1,s2);//m2>m1>s1 ,只有m1和s2有机会成为次大值 
	}
}
void Work2(int x,int i,int &Maxn,int &Sec)
{
	//分三种情况,同上 
	if(Maxn>m3) Sec=Max(m3,Sec);
	if(Maxn==m3) Sec=Max(s3,Sec);
	if(Maxn<m3) Sec=Max(Maxn,s3);
	Maxn=Max(m3,Maxn);
}
ll Find(int x,int y,int z)//上跳求最大值和次大值 
{
	int Maxn=0,Sec=0;
	if(dep[x]<dep[y]) Swap(x,y);
	
	for(int i=20;i>=0;i--){
		if(dep[f[x][i]]>=dep[y]){
			Work2(x,i,Maxn,Sec);//求出x到x的2^i级祖先路径上的最大值和次大值 
			x=f[x][i];
		}
	}
	if(x==y){
		if(Maxn==z) return Sec;
		return Maxn;
	}
	for(int i=20;i>=0;i--){
		if(f[x][i]!=f[y][i]){
			Work2(x,i,Maxn,Sec);//求出x到x的2^i级祖先路径上的最大值和次大值 
			Work2(y,i,Maxn,Sec);//求出y到y的2^i级祖先路径上的最大值和次大值 
			x=f[x][i],y=f[y][i];
		}
	}
	Work2(x,0,Maxn,Sec);//最后x到lca和y到lca 
	Work2(y,0,Maxn,Sec);
	if(Maxn==z) return Sec;
	return Maxn;
}

void DFS(int u,int fa,int pre)
{
	Work1(u,fa,pre);
	for(int i=head[u];i;i=a[i].nxt){
		int v=a[i].to;
		if(v!=fa) DFS(v,u,a[i].val);
	}
}

namespace MST{//求最小生成树 
	bool cmp(G a,G b){return a.w<b.w;}
	void add(int u,int v,int w)
	{
		tot++;
		a[tot].to=v;
		a[tot].val=w;
		a[tot].nxt=head[u];
		head[u]=tot;
	}
	int Find(int x)
	{
		if(x==fa[x]) return x;
		return fa[x]=Find(fa[x]);
	}
	bool Merge(int x,int y)
	{
		int X=Find(x),Y=Find(y);
		if(X==Y) return false;
		fa[X]=Y;return true;
	}
	ll Kruskal()
	{
		ll ans=0;
		int cnt=0;
		sort(g+1,g+1+m,cmp);
		for(int i=1;i<=m;i++){
			if(Merge(g[i].u,g[i].v)){
				cnt++;
				add(g[i].u,g[i].v,g[i].w);
				add(g[i].v,g[i].u,g[i].w);
				vis[i]=true;
				ans+=(ll)g[i].w;
			}
			if(cnt==n-1) break;
		}
		return ans;
	}
}
using namespace MST;
inline int Read()
{
	int x=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+ch-48;
		ch=getchar();
	}
	return x;
}
int main()
{
	n=Read(),m=Read();
	for(int i=1;i<=n;i++) fa[i]=i;//并查集初始化 
	int root=0;
	for(int i=1;i<=m;i++){
		g[i].u=Read(),g[i].v=Read(),g[i].w=Read();
		root=g[i].u;
	}
	ll ans=Kruskal(),tmp=2147483645;
	DFS(root,0,0);
	for(int i=1;i<=m;i++){
		if(!vis[i]&&g[i].u!=g[i].v){//若这条边不在MST中且不是自环 
			tmp=Min(tmp,g[i].w-Find(g[i].u,g[i].v,g[i].w));
		}
	}
	printf("%lld\n",ans+tmp);
	return 0;
}

标签:maxn,int,s1,生成,严格,Sec,m2,Maxn,BJWC2010
来源: https://www.cnblogs.com/Unlimited-Chan/p/16471846.html