BJOI2010 次小生成树

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;  
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值