P4180 [BJWC2010] 严格次小生成树
P4180 [BJWC2010] 严格次小生成树
题目描述
小 C 最近学了很多最小生成树的算法,Prim 算法、Kruskal 算法、消圈算法等等。正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
数据范围:
数据中无向图不保证无自环。
对于
Solution:
又是美味的 LCT 维护最小生成树捏。我们稍微思考一下就能想到我们明显只能换掉一条边,所以我们先使用 LCT 跑一便最小生成树并记录该联通块内的最大值与严格次大值
然后说一下我们为什么要记录次大值:当然是因为非树边的权值有可能等于树边了。
然后这题就被我们解决了(离放假又进一步) ヾ(´∀ ˋ)ノ
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; }