Description
小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。
正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:
如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值)
这下小C蒙了,他找到了你,希望你帮他解决这个问题。
Input
输入第一行包含两个整数N和M,表示无向图的点数与边数。
接下来M行,每行3个数x,y,z表示,点x和点 y之间有一条边,边的权值为z。
Output
输出包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
Hint
【数据规模】
数据中无向图无自环;
50%的数据 N≤2000 M≤3000;
80%的数据 N≤50000 M≤100000;
100%的数据 N≤100000 M≤300000,边权值非负且不超过 10^9。
maintain(x),旋的那个点只用最后maintain一次。
maintain(x),if多了会很慢。
#include<bits/stdc++.h>
using namespace std;
const int Maxn=500005;
namespace FastIO{
const int L=1<<15;
char buffer[L],*S,*T;
inline char gc(){
if(S==T){T=(S=buffer)+fread(buffer,1,L,stdin);
if(S==T)return EOF;}return *S++;
}
inline int getint(){
register char c;register int x=0,f=1;
for(c=gc();c<'0'||c>'9';c=gc()) if(c=='-')f=-1;
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c-'0'),c=gc();
return x*f;
}
}
using FastIO::getint;
using FastIO::gc;
struct Splay{
int rev[Maxn],prt[Maxn],ch[2][Maxn];
int stk[Maxn],val[Maxn],fir[Maxn],sec[Maxn];
#define ls(x) ch[0][x]
#define rs(x) ch[1][x]
bool isroot(int x){
return (ls(prt[x])^x)&&(rs(prt[x])^x);
}
void maintain(int x){
fir[x]=x;sec[x]=-1;
fir[x]=val[fir[ls(x)]]<=val[fir[x]]?fir[x]:fir[ls(x)];
fir[x]=val[fir[rs(x)]]<=val[fir[x]]?fir[x]:fir[rs(x)];
if(val[fir[ls(x)]]^val[fir[x]]){
sec[x]=~sec[x]?sec[x]:fir[ls(x)];
sec[x]=val[fir[ls(x)]]<=val[sec[x]]?sec[x]:fir[ls(x)];
}
if(val[sec[ls(x)]]^val[fir[x]]){
sec[x]=~sec[x]?sec[x]:sec[ls(x)];
sec[x]=val[sec[ls(x)]]<=val[sec[x]]?sec[x]:sec[ls(x)];
}
if(val[fir[x]]^val[x]){
sec[x]=~sec[x]?sec[x]:x;
sec[x]=val[x]<=val[sec[x]]?sec[x]:x;
}
if(val[fir[rs(x)]]^val[fir[x]]){
sec[x]=~sec[x]?sec[x]:fir[rs(x)];
sec[x]=val[fir[rs(x)]]<=val[sec[x]]?sec[x]:fir[rs(x)];
}
if(val[sec[rs(x)]]^val[fir[x]]){
sec[x]=~sec[x]?sec[x]:sec[rs(x)];
sec[x]=val[sec[rs(x)]]<=val[sec[x]]?sec[x]:sec[rs(x)];
}
}
void rotate(int x){
int f=prt[x],gf=prt[f],t=rs(f)==x,son=ch[!t][x];
if(!isroot(f))ch[rs(gf)==f][gf]=x;prt[x]=gf;
ch[t][prt[son]=f]=son,maintain(f);
ch[!t][prt[f]=x]=f; return ;
}
void pushdown(int x){
if(rev[x]){
rev[ls(x)]^=1,rev[rs(x)]^=1;
swap(ls(x),rs(x));
rev[x]^=1;
}
}
void splay(int x){
stk[++stk[0]]=x;for(int i=x;!isroot(i);i=prt[i])stk[++stk[0]]=prt[i];
while(stk[0])pushdown(stk[stk[0]--]);
for(;!isroot(x);rotate(x)){
if(!isroot(prt[x])&&((rs(prt[prt[x]])==prt[x])==(rs(prt[x])==x)))rotate(x);//oj去掉结构体直接单旋过
}
maintain(x);
}
};
struct LinkCutTree{
Splay s;
void access(int x){
for(int lastx=0;x;lastx=x,x=s.prt[x])
s.splay(x),s.rs(x)=lastx,s.maintain(x);
}
void makeroot(int x){
access(x),s.splay(x),s.rev[x]^=1;
}
void link(int x,int y){
makeroot(x),s.prt[x]=y;
}
void split(int x,int y){
makeroot(x),access(y),s.splay(y);
}
}lct;
struct Edge{
int x,y,v;
bool operator <(const Edge&rhs)const{
return v<rhs.v;
}
}e[Maxn];
long long mn,totv[Maxn*3];
int n,m,cnt,tot,fa[Maxn],vst[Maxn];
int getfa(int x){
return x!=fa[x]?fa[x]=getfa(fa[x]):x;
}
void Kruskal(){
cnt=n;sort(e+1,e+m+1);
for(int i=1;i<=n;++i)fa[i]=i;
for(int i=1;i<=m;++i){
int x=e[i].x,y=e[i].y;
if(getfa(x)==getfa(y))continue;
vst[i]=1;
mn+=e[i].v;
fa[getfa(x)]=getfa(y);
lct.s.val[++cnt]=e[i].v;
lct.link(cnt,x),lct.link(cnt,y);
}
}
int main(){
memset(lct.s.val,-1,sizeof(lct.s.val));
n=getint(),m=getint();
for(int i=1;i<=m;++i){
int x=getint(),y=getint();
e[i]=(Edge){x,y,getint()};
}
Kruskal();
for(int i=1;i<=m;++i)if(!vst[i]){
int x=e[i].x,y=e[i].y;
lct.split(x,y);
int fir=lct.s.fir[y],sec=lct.s.sec[y];
if(~fir)totv[++tot]=mn-lct.s.val[fir]+e[i].v;
if(~sec)totv[++tot]=mn-lct.s.val[sec]+e[i].v;
}
sort(totv+1,totv+tot+1);
for(int i=1;i<=tot;++i)
if(totv[i]!=mn){cout<<totv[i]<<endl;break;}
return 0;
}