洛谷P4180 [BJWC2010]严格次小生成树

7 篇文章 0 订阅
3 篇文章 0 订阅

传送门

之前写过一次,但是理解不深刻,复习之后有了更加细节的一些理解

好了进入正题

首先,我们需要知道次小生成树一定是在最小生成树的邻集中,即次小生成树与最小生成树只会有一条边的差别

所以我们会想枚举所有非树边,看看哪条换进去可以得到次小生成树

而非树边去替换哪条树边又是一个问题,但是如果你注意到当前这条非树边与树上的一些树边构成了环——其实也就是这条非树边的两端点的LCA与之成环了

意识到这一点,问题就会简单许多,我们可以利用LCA中的倍增思想,维护路径上的权值最大边

事实上,我们还需要维护路径上的权值次大边——因为要求严格最小,不允许权值和相等(否则怎么叫次小呢),而如果权值最大边与我们枚举到的当前非树边全职相等,那就替换了个寂寞~。于是我们需要维护严格次大,如果没有次大那就是没有,一定要保证严格性

#include<bits/stdc++.h>
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
	int register data=0;static char ch=0;ch=nc();
	while(!isdigit(ch))ch=nc();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=nc();
	return data;
}
const int N=1e5+1,M=3e5+1,inf=1e9;
int cnt,first[N],n,m,fa[N],dep[N],fir[N][18],sec[N][18],f[N][18],mnm;
long long sum;
bool vis[M];
struct edge{int v,w,nxt;}e[M<<1];
struct Edge{int u,v,w;}E[M];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline bool cmp(Edge x,Edge y){return x.w<y.w;}
inline void add(int u,int v,int w){e[++cnt]=(edge){v,w,first[u]},first[u]=cnt;}
inline void dfs(int u){
	for(int register i=1;(1<<i)<=dep[u];++i){
		f[u][i]=f[f[u][i-1]][i-1],fir[u][i]=max(fir[f[u][i-1]][i-1],fir[u][i-1]);
		if(fir[f[u][i-1]][i-1]!=fir[u][i-1])sec[u][i]=min(fir[f[u][i-1]][i-1],fir[u][i-1]);
		sec[u][i]=max(sec[u][i],max(sec[f[u][i-1]][i-1],sec[u][i-1]));
	}
	for(int register v,i=first[u];i;i=e[i].nxt){
		v=e[i].v;
		if(dep[v])continue;
		dep[v]=dep[u]+1,fir[v][0]=e[i].w,f[v][0]=u,dfs(v);
	}
}
typedef pair<int,int> T;
inline T lca(int a,int b){
	if(dep[a]<dep[b])swap(a,b);
	int register gap=dep[a]-dep[b],_1=0,_2=0;
	for(int register i=0;(1<<i)<=gap;++i)
		if(gap&(1<<i)){
			if(_1!=fir[a][i])_2=max(_2,min(_1,fir[a][i]));
			_1=max(_1,fir[a][i]),_2=max(_2,sec[a][i]),a=f[a][i];
		}
	if(a==b)return make_pair(_1,_2);
	for(int register i=17;i>=0;--i)
		if(f[a][i]!=f[b][i]){
			if(_1!=fir[a][i])_2=max(_2,min(_1,fir[a][i]));
			_1=max(_1,fir[a][i]),_2=max(_2,sec[a][i]),a=f[a][i];
			if(_1!=fir[b][i])_2=max(_2,min(_1,fir[b][i]));
			_1=max(_1,fir[b][i]),_2=max(_2,sec[b][i]),b=f[b][i];
		}
	if(_1!=fir[a][0])_2=max(_2,min(_1,fir[a][0]));
	if(_1!=fir[b][0])_2=max(_2,min(_1,fir[b][0]));
	_1=max(_1,max(fir[a][0],fir[b][0])),_2=max(_2,max(sec[a][0],sec[b][0]));
	return make_pair(_1,_2);
}
signed main(){
	mnm=inf,n=rd(),m=rd();
	for(int register i=1;i<=m;++i)E[i].u=rd(),E[i].v=rd(),E[i].w=rd();
	sort(E+1,E+m+1,cmp);
	for(int register i=1;i<=n;++i)fa[i]=i;
	for(int register u,v,w,fu,fv,i=1;i<=m;++i){
		u=E[i].u,v=E[i].v,w=E[i].w,fu=find(u),fv=find(v);
		if(fu!=fv)sum+=w,vis[i]=1,fa[fu]=fv,add(u,v,w),add(v,u,w);
	}dep[1]=1,dfs(1);
	for(int register i=1;i<=m;++i){
		if(vis[i])continue;
		int register u=E[i].u,v=E[i].v,w=E[i].w;
		T register t=lca(u,v);
		if(w!=t.first)mnm=min(mnm,w-t.first);
		else mnm=min(mnm,w-t.second);
	}cout<<sum+mnm,exit(0);
}

upd:
本来可以进水谷第一页的(即使不加fread),万万没想到有一个人疯狂提交AC代码,强行把我挤出了第一页这是提交记录第二页

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值