P4180 [BJWC2010] 严格次小生成树

P4180 [BJWC2010] 严格次小生成树

题目描述

小 C 最近学了很多最小生成树的算法,Prim 算法、Kruskal 算法、消圈算法等等。正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e 的权值) eEMvalue(e)<eESvalue(e)

这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

数据范围:

数据中无向图不保证无自环

对于 100% 的数据, N105M3×105,边权 [0,109],数据保证必定存在严格次小生成树。

Solution:

又是美味的 LCT 维护最小生成树捏。我们稍微思考一下就能想到我们明显只能换掉一条边,所以我们先使用 LCT 跑一便最小生成树并记录该联通块内的最大值与严格次大值 mx0,mx1,然后枚举每一条 非树边 ,判断其与 mx0,mx1 的大小关系然后更新答案,但是不用换边 因为 我们明显只能换掉一条边

然后说一下我们为什么要记录次大值:当然是因为非树边的权值有可能等于树边了。

然后这题就被我们解决了(离放假又进一步) ヾ(´∀ ˋ)ノ

Code:

#include<bits/stdc++.h>
#define ll long long
const int N=4e5+5;
const ll inf=1e17;
using namespace std;
struct LCT{
int w[N],st[N];
struct Tree{
int tag,ff,ch[2],mx[2];
}t[N];
#define ls t[x].ch[0]
#define rs t[x].ch[1]
#define fa t[x].ff
inline bool isroot(int x)
{
return (t[fa].ch[0]==x||t[fa].ch[1]==x);
}
inline void upd(int x,int y)
{
if(t[x].mx[0]<t[y].mx[0])
{
t[x].mx[1]=t[x].mx[0];
t[x].mx[0]=t[y].mx[0];
if(t[x].mx[1]<t[y].mx[1])
{
t[x].mx[1]=t[y].mx[1];
}
}
else
{
if(t[x].mx[1]<t[y].mx[0])
{
t[x].mx[1]=t[y].mx[0];
}
}
}
inline void pushup(int x)
{
t[x].mx[0]=w[x];
if(ls)upd(x,ls);
if(rs)upd(x,rs);
return;
}
inline void rev(int x)
{
swap(t[x].ch[0],t[x].ch[1]);
t[x].tag^=1;
return ;
}
inline void pushdown(int x)
{
if(t[x].tag)
{
if(ls)rev(ls);
if(rs)rev(rs);
t[x].tag=0;
}
return ;
}
inline void rotate(int x)
{
int y=fa,z=t[fa].ff,k=t[fa].ch[1]==x ? 1 : 0;
if(isroot(y))t[z].ch[t[z].ch[1]==y]=x;
t[x].ff=z;
t[y].ch[k]=t[x].ch[!k];
if(t[x].ch[!k])t[t[x].ch[!k]].ff=y;
t[x].ch[!k]=y;
t[y].ff=x;
pushup(y);
}
inline void splay(int x)
{
int y=x,z=0;
st[++st[0]]=y;
while(isroot(y))st[++st[0]]=y=t[y].ff;
while(st[0])pushdown(st[st[0]--]);
while(isroot(x))
{
y=fa,z=t[fa].ff;
if(isroot(y)){rotate((t[y].ch[1]==x)==(t[z].ch[1]==y) ? y : x);}
rotate(x);
}
pushup(x);
}
void access(int x)
{
int y=0;
while(x)
{
splay(x);rs=y;pushup(x);
y=x;x=fa;
}
}
void make_root(int x)
{
access(x);splay(x);
rev(x);
}
int find(int x)
{
access(x);splay(x);
while(ls)pushdown(x),x=ls;
splay(x);
return x;
}
void splite(int x,int y)
{
make_root(x);
access(y);splay(y);
}
void link(int x,int y)
{
make_root(x);
if(find(y)!=x)t[x].ff=y;
return ;
}
void cut(int x) // 这里是减去一个点 x
{
splay(x);
t[ls].ff=t[rs].ff=0;
return ;
}
bool check(int x,int y)
{
make_root(x);
return find(y)==x;
}
}T;
struct Edge{
int u,v,w;
bool operator <(const Edge &e)const{
return w<e.w;
}
};
vector<Edge> E;
int n,m,tot,cnt=0;
ll ans,tmp=0;
int tag[N];
void work()
{
cin>>n>>m;ans=inf;
tot=n;
for(int i=1,u,v,w;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
E.push_back((Edge){u,v,w});
}
sort(E.begin(),E.end());
for(auto [u,v,w] : E)
{
T.w[++tot]=w;
if(!T.check(u,v))
{
T.link(u,tot);T.link(tot,v);cnt++;tmp+=w;
tag[tot]=1;
}
}
tot=n;
for(auto [u,v,w] : E)
{
if(tag[++tot]||u==v)continue;
T.splite(u,v);
int mx[2]={T.t[v].mx[0],T.t[v].mx[1]};
if(w>mx[0]&&mx[0])
{
ans=min(ans,tmp-mx[0]+w);
}
else if(w>mx[1]&&mx[1])
{
ans=min(ans,tmp-mx[1]+w);
}
}
printf("%lld\n",ans);
}
int main()
{
//freopen("P4180_1.in","r",stdin);freopen("tree.out","w",stdout);
work();
return 0;
}
posted @   liuboom  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示