【BJOI2010】次小生成树

【BJOI2010】次小生成树

【BJOI2010】次小生成树

Description

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

Input

输入文件tree.in第一行包含两个整数N和M,表示无向图的点数与边数。接下来M行,每行3个数x,y,z表示,点x和点 y之间有一条边,边的权值为z。

Output

输出文件 tree.out 包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)                          

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。                                                                                                          

Solution

题面很清晰,求严格的次小生成树。
如果是简单的最小生成树,那么用Prim或者Kruskal都可以解决。
如果只是求次小生成树的话,枚举每一条没有被选中的边(x,y),查询x->y的路径中的最大边,连接xy之后删除
最大边就可以得到次小的生成树的一种可能性,选取最后值最小的一个就是次小生成树。
但是在求严格次小生成树的时候,如果x到y的路径上的最大边和枚举的边相同时,所得生成树不是严格次小的。所以我们还要记录一下路径上的次大边来解决这种问题。
维护路径最大和次大值。。。蒟蒻忘了 (不会)LCA,所以用了树剖。。。 

CODE

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
inline int read(){
	char c;int rec=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec;
}
int n,m;
long long ans=0;
struct Edge {int a,b,val,flag;}edge[300005];
inline bool cmp(Edge x,Edge y){return x.val<y.val;}
int pre[100005];
inline int getfa(int x){
	if(x==pre[x])return x;
	return pre[x]=getfa(pre[x]);
}
struct Branch {int next,to,val;}branch[200005];
int h[100005],cnt=0;
inline void add(int x,int y,int z){
	branch[++cnt].to=y;branch[cnt].next=h[x];h[x]=cnt;branch[cnt].val=z;return ;
}
inline void Kruskal(){
	int k=0;
	for(int i=1;i<=m;i++){
		int x=edge[i].a,y=edge[i].b,z=edge[i].val;
		int fx=getfa(x),fy=getfa(y);
		if(fx==fy)continue;
		else {
			edge[i].flag=1;
			pre[fx]=fy;
			k++;ans+=z;
			add(x,y,z);add(y,x,z);
		}
		if(k==n-1)break;
	}return ;
}
int len[100005];
int size[100005],deep[100005];
int top[100005],fa[100005],son[100005];
int id[100005],pos[100005];
inline void Dfs1(int v,int pre,int dep){
	size[v]=1;deep[v]=dep;fa[v]=pre;
	for(int i=h[v];i;i=branch[i].next){
		int j=branch[i].to;
		if(j==pre)continue;
		Dfs1(j,v,dep+1);
		size[v]+=size[j];
		len[j]=branch[i].val;
		if(size[son[v]]<size[j])son[v]=j;
	}return ;
}
inline void Dfs2(int v,int T){
	top[v]=T;id[v]=++cnt;pos[cnt]=v;
	if(son[v])Dfs2(son[v],T);
	for(int i=h[v];i;i=branch[i].next){
		int j=branch[i].to;
		if(j==fa[v]||j==son[v])continue;
		Dfs2(j,j);
	}return ;
}
struct Seg_Tree{
	int L,R,maxx,semx;
}tree[100000<<2];
inline void Build(int v,int L,int R){
	tree[v].L=L;tree[v].R=R;
	if(L==R){tree[v].maxx=len[pos[L]];return ;}
	int mid=(L+R)>>1;
	Build(v<<1,L,mid);Build(v<<1|1,mid+1,R);
	int f1,f2,s1,s2;
	f1=tree[v<<1].maxx;f2=tree[v<<1|1].maxx;
	s1=tree[v<<1].semx;s2=tree[v<<1|1].semx;
	tree[v].maxx=max(f1,f2);
	if(f1>f2){
		if(s1>f2)tree[v].semx=s1;
		else tree[v].semx=f2;
	}
	if(f1==f2)tree[v].semx=max(s1,s2);
	if(f1<f2){
		if(s2>f1)tree[v].semx=s2;
		else tree[v].semx=f1;
	}
	return ;
}
int q1,q2;
inline void Updata(int v){
	int f1=tree[v].maxx,f2=tree[v].semx;
	if(f1>q1){q2=max(q1,f2);q1=f1;}
	if(f1==q1)q2=max(q2,f2);
	if(f1<q1)q2=max(q2,f1);
	return ;
}
inline void Query(int v,int L,int R){
	if(tree[v].L>R||tree[v].R<L)return ;
	if(tree[v].L>=L&&tree[v].R<=R){Updata(v);return ;}
	Query(v<<1,L,R);Query(v<<1|1,L,R);
}
inline int Lca(int x,int y){
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		x=fa[top[x]];
	}if(deep[x]<deep[y])swap(x,y);
	return y;
}
int delta=0x3f3f3f3f;
inline void Sov(int v){
	int x=edge[v].a,y=edge[v].b,z=edge[v].val;
	int p=Lca(x,y);q1=q2=0;
	int maxx=0;
	while(top[x]!=top[p]){
		Query(1,id[top[x]],id[x]);
		x=fa[top[x]];
	}if(deep[p]+1<=deep[x])Query(1,id[son[p]],id[x]);
	while(top[y]!=top[p]){
		Query(1,id[top[y]],id[y]);
		y=fa[top[y]];
	}if(deep[p]+1<=deep[y])Query(1,id[son[p]],id[y]);
	if(q1==z)delta=min(z-q2,delta);
	else delta=min(z-q1,delta);
	return ;
}
int main(){
	n=read();m=read();
	for(int i=1;i<=m;i++)
	    {edge[i].a=read();edge[i].b=read();edge[i].val=read();edge[i].flag=0;}
	sort(edge+1,edge+1+m,cmp);
	for(int i=1;i<=n;i++)pre[i]=i;
	Kruskal();cnt=0;
	Dfs1(1,0,1);Dfs2(1,1);
	Build(1,2,n);
	for(int i=1;i<=m;i++)
		if(!edge[i].flag)Sov(i);
	cout<<ans+delta;
	return 0;
}
树剖维护边权的时候求一下LCA,最后判断一下,是否该加上最后一段,而不会多加上额外的边权。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值