[BeiJing2010组队]次小生成树Tree

Time Limit: 10 Sec Memory Limit: 512 MB

Description

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

\(80\%\) 的数据\(N \leq 50000\) \(M \leq 100000\)

\(100\%\)的数据\(N \leq 100000\) \(M \leq 300000\) ,边权值非负且不超过 \(10^9\)

Solution

首先有一个结论,次小生成树与最小生成树一定只相差一条边。自己大概想象一下,如果相差多余一条边的话,那么要么那个最小生成树不是最小的,要么次小生成树不是次小的,我们都可以拿多出来的两条边和原来的树里面的边拼拼凑凑得到一个更优的最小或者次小生成树。

所以我们只需要枚举是哪一条边即可。然后,我们如果加入这条边,使得变化量最小。我们加入这条边以后,一定会与树里面原来的边形成一个环,为了让变化量最小,我们只需要抛弃掉除了这条边以外的最长的边。但是有一个问题,题目要的是严格次小生成树,所以如果这条边与原来环里面的边边权最大值想等的话,我们只能抛弃次大值了。而这个最大值的维护则是树上做LCA的套路了,次大值就跟着一起慢慢维护吧,这里可能比较繁琐。

这道题写的时候可能要想一想如何更新次大值,比较细节化。嗯不过今天写的时候可能rp好了一点,尽然过了样例以后交上去一遍A掉。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
#define REP(i,a,n) for(register int i=(a);i<=(n);++i)
#define PER(i,a,n) for(register int i=(a);i>=(n);--i)
#define FEC(i,x) for(register int i=head[x];i;i=g[i].ne)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
#define lc o<<1
#define rc o<<1|1
namespace io{
	const int SIZE=(1<<21)+1;char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55];int f,qr;
	#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
	inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
	inline void putc(char x){*oS++=x;if(oS==oT)flush();}
	template<class I>inline void read(I &x){for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;}
	template<class I>inline void write(I x){if(!x)putc('0');if(x<0)putc('-'),x=-x;while(x)qu[++qr]=x%10+'0',x/=10;while(qr)putc(qu[qr--]);}
	struct Flusher_{~Flusher_(){flush();}}io_flusher_;
}//orz laofudasuan
using io::read;using io::putc;using io::write;
typedef long long ll;typedef unsigned long long ull;
template<typename A,typename B>inline bool SMAX(A&x,const B&y){return x<y?x=y,1:0;}
template<typename A,typename B>inline bool SMIN(A&x,const B&y){return y<x?x=y,1:0;}

const int N=100000+7,M=300000+7,LOG=18;const ll INF=0x3f3f3f3f3f3f3f3f;
int n,m,x,y,z;ll ans=INF,sum=0;int dep[N],f[N][LOG],s[N][LOG],s2[N][LOG],intr[M];
struct Graph{int x,y,z;inline bool operator<(const Graph&a)const{return z<a.z;}}G[M];
struct Edge{int to,ne,w;}g[N<<1];int head[N],tot;
inline void Addedge(int x,int y,int z){g[++tot].to=y;g[tot].w=z;g[tot].ne=head[x];head[x]=tot;}

int fa[N];
inline int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);}
inline void Union(int x,int y){x=Find(x),y=Find(y);fa[y]=x;}
inline void Kruskal(){
	sort(G+1,G+m+1);for(register int i=1;i<=n;++i)fa[i]=i;int cnt=0;
	for(register int i=1;i<=m;++i){
		int x=Find(G[i].x),y=Find(G[i].y),z=G[i].z;if(x==y)continue;
		intr[i]=1;Union(x,y);Addedge(G[i].x,G[i].y,z),Addedge(G[i].y,G[i].x,z);sum+=z;++cnt;if(cnt==n-1)return;
	}
}

inline void DFS(int x,int fa=0){
	dep[x]=dep[fa]+1;f[x][0]=fa;for(register int i=1;i<LOG;++i){
		f[x][i]=f[f[x][i-1]][i-1];
		if(s[x][i-1]>s[f[x][i-1]][i-1]){
			s[x][i]=s[x][i-1];
			if(s2[x][i-1]>s[f[x][i-1]][i-1])s2[x][i]=s2[x][i-1];
			else s2[x][i]=s[f[x][i-1]][i-1];
		}
		else if(s[x][i-1]<s[f[x][i-1]][i-1]){
			s[x][i]=s[f[x][i-1]][i-1];
			if(s2[f[x][i-1]][i-1]>s[x][i-1])s2[x][i]=s2[f[x][i-1]][i-1];
			else s2[x][i]=s[x][i-1];
		}
		else s[x][i]=s[x][i-1],s2[x][i]=max(s2[x][i],s2[f[x][i-1]][i]);
	}
	for(register int i=head[x];i;i=g[i].ne)if(g[i].to!=fa)s[g[i].to][0]=g[i].w,DFS(g[i].to,x);
}
struct Pair{int x,y;};
inline Pair LCA(int x,int y){
	if(dep[x]<dep[y])x^=y^=x^=y;int ans=0,ans2=0;
	for(register int i=LOG-1;i>=0;--i)if(dep[f[x][i]]>=dep[y]){
		if(s[x][i]>ans){if(s2[x][i]>ans)ans=s[x][i],ans2=s2[x][i];else ans2=ans,ans=s[x][i];}
		else if(s[x][i]<=ans){if(s[x][i]>ans2)ans2=s[x][i];}
		x=f[x][i];
	}
	if(x==y)return Pair{ans,ans2};
	for(register int i=LOG-1;i>=0;--i)if(f[x][i]!=f[y][i]){
		if(s[x][i]>ans){if(s2[x][i]>ans)ans=s[x][i],ans2=s2[x][i];else ans2=ans,ans=s[x][i];}
		else if(s[x][i]<=ans){if(s[x][i]>ans2)ans2=s[x][i];}
		if(s[y][i]>ans){if(s2[y][i]>ans)ans=s[y][i],ans2=s2[y][i];else ans2=ans,ans=s[y][i];}
		else if(s[y][i]<=ans){if(s[y][i]>ans2)ans2=s[y][i];}
		x=f[x][i];y=f[y][i];
	}
	if(s[x][0]>ans){if(s2[x][0]>ans)ans=s[x][0],ans2=s2[x][0];else ans2=ans,ans=s[x][0];}
	else if(s[x][0]<=ans){if(s[x][0]>ans2)ans2=s[x][0];}
	if(s[y][0]>ans){if(s2[y][0]>ans)ans=s[y][0],ans2=s2[y][0];else ans2=ans,ans=s[y][0];}
	else if(s[y][0]<=ans){if(s[y][0]>ans2)ans2=s[y][0];}
	x=f[x][0];y=f[y][0];return Pair{ans,ans2};
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("BZOJ1977.in","r",stdin);freopen("BZOJ1977.out","w",stdout);
#endif
	read(n),read(m);
	for(register int i=1;i<=m;++i)read(G[i].x),read(G[i].y),read(G[i].z);
	Kruskal();DFS(1);
	for(register int i=1;i<=m;++i)if(!intr[i]){
		x=G[i].x,y=G[i].y,z=G[i].z;
		Pair p=LCA(x,y);
		if(z>p.x)SMIN(ans,sum-p.x+z);
		else if(z==p.x&&z>p.y)SMIN(ans,sum-p.y+z);
	}
	write(ans);putc('\n');
}

Update:又被人叉WA掉了,上面那个程序有一个细节没处理好,还有一处小错误,下面是修改后的代码,却不知道为什么突然变慢了。目前测过的几个OJ(BZOJ,LOJ,洛谷)数据都太弱了,大家可以试一下这组数据:

7 7
3 5 4
4 6 4
1 2 5
1 3 5
2 4 5
5 7 5
6 7 5

正确答案是29

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
#define REP(i,a,n) for(register int i=(a);i<=(n);++i)
#define PER(i,a,n) for(register int i=(a);i>=(n);--i)
#define FEC(i,x) for(register int i=head[x];i;i=g[i].ne)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
#define lc o<<1
#define rc o<<1|1
namespace io{
	const int SIZE=(1<<21)+1;char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55];int f,qr;
	#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
	inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
	inline void putc(char x){*oS++=x;if(oS==oT)flush();}
	template<class I>inline void read(I &x){for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;}
	template<class I>inline void write(I x){if(!x)putc('0');if(x<0)putc('-'),x=-x;while(x)qu[++qr]=x%10+'0',x/=10;while(qr)putc(qu[qr--]);}
	struct Flusher_{~Flusher_(){flush();}}io_flusher_;
}//orz laofudasuan
using io::read;using io::putc;using io::write;
typedef long long ll;typedef unsigned long long ull;
template<typename A,typename B>inline bool SMAX(A&x,const B&y){return x<y?x=y,1:0;}
template<typename A,typename B>inline bool SMIN(A&x,const B&y){return y<x?x=y,1:0;}

const int N=100000+7,M=300000+7,LOG=18;const ll INF=0x3f3f3f3f3f3f3f3f;
int n,m,x,y,z;ll ans=INF,sum=0;int dep[N],f[N][LOG],s[N][LOG],s2[N][LOG],intr[M];
struct Graph{int x,y,z;inline bool operator<(const Graph&a)const{return z<a.z;}}G[M];
struct Edge{int to,ne,w;}g[N<<1];int head[N],tot;
inline void Addedge(int x,int y,int z){g[++tot].to=y;g[tot].w=z;g[tot].ne=head[x];head[x]=tot;}

int fa[N];
inline int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);}
inline void Union(int x,int y){x=Find(x),y=Find(y);fa[y]=x;}
inline void Kruskal(){
	sort(G+1,G+m+1);for(register int i=1;i<=n;++i)fa[i]=i;int cnt=0;
	for(register int i=1;i<=m;++i){
		int x=Find(G[i].x),y=Find(G[i].y),z=G[i].z;if(x==y)continue;
		intr[i]=1;Union(x,y);Addedge(G[i].x,G[i].y,z),Addedge(G[i].y,G[i].x,z);sum+=z;++cnt;if(cnt==n-1)return;
	}
}

inline void DFS(int x,int fa=0){
	dep[x]=dep[fa]+1;f[x][0]=fa;for(register int i=1;i<LOG;++i){
		f[x][i]=f[f[x][i-1]][i-1];
		if(s[x][i-1]>s[f[x][i-1]][i-1]){
			s[x][i]=s[x][i-1];
			if(s2[x][i-1]>s[f[x][i-1]][i-1])s2[x][i]=s2[x][i-1];
			else s2[x][i]=s[f[x][i-1]][i-1];
		}
		else if(s[x][i-1]<s[f[x][i-1]][i-1]){
			s[x][i]=s[f[x][i-1]][i-1];
			if(s2[f[x][i-1]][i-1]>s[x][i-1])s2[x][i]=s2[f[x][i-1]][i-1];
			else s2[x][i]=s[x][i-1];
		}
		else s[x][i]=s[x][i-1],s2[x][i]=max(s2[x][i-1],s2[f[x][i-1]][i-1]);
	}
	for(register int i=head[x];i;i=g[i].ne)if(g[i].to!=fa)s[g[i].to][0]=g[i].w,DFS(g[i].to,x);
}
struct Pair{int x,y;};
inline Pair LCA(int x,int y){
	if(dep[x]<dep[y])x^=y^=x^=y;int ans=0,ans2=0;
	for(register int i=LOG-1;i>=0;--i)if(dep[f[x][i]]>=dep[y]){
		if(s[x][i]>ans){if(s2[x][i]>ans)ans=s[x][i],ans2=s2[x][i];else ans2=ans,ans=s[x][i];}
		else if(s[x][i]<=ans){if(s[x][i]>ans2)ans2=s[x][i];}
		x=f[x][i];
	}
	if(x==y)return Pair{ans,ans2};
	for(register int i=LOG-1;i>=0;--i)if(f[x][i]!=f[y][i]){
		if(s[x][i]>ans){if(s2[x][i]>ans)ans=s[x][i],ans2=s2[x][i];else ans2=ans,ans=s[x][i];}
		else if(s[x][i]<=ans){if(s[x][i]>ans2&&s[x][i]!=ans)ans2=s[x][i];}
		if(s[y][i]>ans){if(s2[y][i]>ans)ans=s[y][i],ans2=s2[y][i];else ans2=ans,ans=s[y][i];}
		else if(s[y][i]<=ans){if(s[y][i]>ans2&&s[y][i]!=ans)ans2=s[y][i];}
		x=f[x][i];y=f[y][i];
	}
	if(s[x][0]>ans){if(s2[x][0]>ans)ans=s[x][0],ans2=s2[x][0];else ans2=ans,ans=s[x][0];}
	else if(s[x][0]<=ans){if(s[x][0]>ans2&&s[x][0]!=ans)ans2=s[x][0];}
	if(s[y][0]>ans){if(s2[y][0]>ans)ans=s[y][0],ans2=s2[y][0];else ans2=ans,ans=s[y][0];}
	else if(s[y][0]<=ans){if(s[y][0]>ans2&&s[y][0]!=ans)ans2=s[y][0];}//错误笔记:必须判断一下是s[y][0]!=ans,才能保证严格 
	x=f[x][0];y=f[y][0];return Pair{ans,ans2};
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("BZOJ1977.in","r",stdin);freopen("BZOJ1977.out","w",stdout);
#endif
	read(n),read(m);
	for(register int i=1;i<=m;++i)read(G[i].x),read(G[i].y),read(G[i].z);
	Kruskal();DFS(1);
	for(register int i=1;i<=m;++i)if(!intr[i]){
		x=G[i].x,y=G[i].y,z=G[i].z;
		Pair p=LCA(x,y);
		if(z>p.x)SMIN(ans,sum-p.x+z);
		else if(z==p.x&&z>p.y)SMIN(ans,sum-p.y+z);
	}
	write(ans);putc('\n');
}
posted @ 2018-10-24 19:48  hankeke303  阅读(241)  评论(0编辑  收藏  举报