【BZOJ1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+倍增

【BZOJ1977】[BeiJing2010组队]次小生成树 Tree

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≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

题解:结论!次小生成树一定可以由 最小生成树+一条非树边-这条非树边覆盖的一条边 得到。证明方法。。。假设我们要加入两条边,先加一条边a使总权值变大,再加一条边b使权值变小。那么b所替换掉的边一定在a形成的环上,因为a是非树边,所以a的权值比环上的所有边都大,所以b替换掉的边权值一定不会大于a,所以我们就白加入a这条边了。(奇怪的证明)

所以我们要替换那条边呢?如果它所覆盖的边的最大值=这条非树边的权值,则替换它所覆盖的边的次大值;如果不相等,则直接替换最大值。这就需要我们求出一条路径上的权值最大值和次大值,可以用树剖,当然我觉得倍增更优美一点~

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=100010;
int n,m,cnt;
ll ans,sum;
struct edge
{
	int a,b,val,used;
}p[maxn*3];
struct node
{
	int x,y;
	node() {x=0,y=-1;}
	node(int _){x=_,y=0;}
	node operator + (const node &a)const
	{
		node b;
		if(x<a.x)	b.x=a.x,b.y=max(x,a.y);
		if(x>a.x)	b.x=x,b.y=max(y,a.x);
		if(x==a.x)	b.x=x,b.y=max(y,a.y);
		return b;
	}
}s[19][maxn];
int to[maxn<<1],next[maxn<<1],head[maxn],val[maxn<<1],fa[19][maxn],dep[maxn],Log[maxn],f[maxn];
bool cmp(const edge &a,const edge &b)
{
	return a.val<b.val;
}
int find(int x)
{
	return (f[x]==x)?x:f[x]=find(f[x]);
}
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
inline node ask(int a,int b)
{
	if(dep[a]<dep[b])	swap(a,b);
	node ret;
	for(int i=Log[dep[a]-dep[b]];i>=0;i--)	if(dep[fa[i][a]]>=dep[b])	ret=ret+s[i][a],a=fa[i][a];
	if(a==b)	return ret;
	for(int i=Log[dep[a]];i>=0;i--)	if(fa[i][a]!=fa[i][b])	ret=ret+s[i][a]+s[i][b],a=fa[i][a],b=fa[i][b];
	return ret+s[0][a]+s[0][b];
}
void dfs(int x)
{
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[0][x])
		fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,s[0][to[i]]=node(val[i]),dfs(to[i]);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	int i,j,a,b;
	for(i=1;i<=m;i++)	p[i].a=rd(),p[i].b=rd(),p[i].val=rd();
	sort(p+1,p+m+1,cmp);
	for(i=1;i<=n;i++)	f[i]=i;
	memset(head,-1,sizeof(head));
	for(i=1;i<=m;i++)
	{
		a=find(p[i].a),b=find(p[i].b);
		if(a!=b)
		{
			add(p[i].a,p[i].b,p[i].val),add(p[i].b,p[i].a,p[i].val),f[a]=b,p[i].used=1,sum+=p[i].val;
			if(cnt==(n-1)<<1)	break;
		}
	}
	dep[1]=1,dfs(1);
	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
	for(j=1;(1<<j)<=n;j++)	for(i=1;i<=n;i++)	fa[j][i]=fa[j-1][fa[j-1][i]],s[j][i]=s[j-1][i]+s[j-1][fa[j-1][i]];
	ans=1ll<<60;
	for(i=1;i<=m;i++)	if(!p[i].used)
	{
		node tmp=ask(p[i].a,p[i].b);
		if(tmp.x!=p[i].val&&tmp.x>0)	ans=min(ans,sum+p[i].val-tmp.x);
		if(tmp.y>0)	ans=min(ans,sum+p[i].val-tmp.y);
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2017-09-27 20:39  CQzhangyu  阅读(281)  评论(0编辑  收藏  举报