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

1977: [BeiJing2010组队]次小生成树 Tree

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 2313   Solved: 544
[ Submit][ Status][ Discuss]

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

数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。


倍增。


求出最小生成树,枚举每一条非树边,用它取代掉环上最大的边最优。


那么相当于求两点到lca的最大边权,直接倍增做。


但是要注意是严格最小生成树,那么这条非树边与环上最大边相同时可能取代掉的是环上的次大边,那么倍增的时候多维护一个严格次大的边即可。


#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstring>
#define LL long long
#define pa pair<int,int>
#define inf 1000000005
#define M 300000+5
#define N 100000+5
#include <queue>
using namespace std;
queue<int> q;
LL pre=0;
int a[5],fa[N],v[M],n,m,tot,h[N],d[N],f[N][21];
struct Edge
{
	int x,y,v;
}E[M];
struct edge
{
	int y,ne,v;
}e[M*2];
struct ma
{
	int a,b;
}g[N][21],p;
bool cmp(Edge a,Edge b)
{
	return a.v<b.v;
}
int Getfather(int x)
{
	return x==fa[x]?x:fa[x]=Getfather(fa[x]);
}
void Kruscal()
{
	sort(E+1,E+1+m,cmp);
	int cnt=1;
	for (int i=1;i<=m;i++)
	{
		int fx=Getfather(E[i].x),fy=Getfather(E[i].y);
		if (fx==fy) continue;
		cnt++;
		v[i]=1;
		fa[fx]=fy;
		pre+=E[i].v;
	}
}
void Addedge(int x,int y,int v)
{
	e[++tot].y=y;
	e[tot].ne=h[x];
	e[tot].v=v;
	h[x]=tot;
	e[++tot].y=x;
	e[tot].ne=h[y];
	e[tot].v=v;
	h[y]=tot;
}
void Build()
{
	for (int i=1;i<=m;i++)
		if (v[i])
			Addedge(E[i].x,E[i].y,E[i].v);
	g[1][0].a=g[1][0].b=-inf;
	f[1][0]=0;
	d[1]=1;
	q.push(1);
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		for (int i=h[x];i;i=e[i].ne)
		{
			int y=e[i].y;
			if (y==f[x][0]) continue;
			f[y][0]=x;
			d[y]=d[x]+1;
			g[y][0].a=e[i].v;
			q.push(y);
		}
	}
}
void Update(ma &x,ma y)
{
	/*a[0]=x.a,a[1]=x.b,a[2]=y.a,a[3]=y.b;
	sort(a,a+4);
	x.a=max(x.a,y.a);
	for (int k=2;k>=0;k--)
		if (a[k]!=a[k+1])
		{
			x.b=a[k];
			break;
		}*/
	if (x.a>y.a) x.b=max(y.a,x.b);
	else if (x.a<y.a) x.b=max(x.a,y.b);
	else x.b=max(x.b,y.b);
	x.a=max(x.a,y.a);
}
void ST()
{
	for (int j=1;(1<<j)<=n;j++)
		for (int i=1;i<=n;i++)
		{
			f[i][j]=f[f[i][j-1]][j-1];
			Update(g[i][j],g[i][j-1]);
			Update(g[i][j],g[f[i][j-1]][j-1]);
		}
}
void Move(int &x,int deep)
{
	for (int i=20;i>=0;i--)
		if (d[f[x][i]]>=deep)
			Update(p,g[x][i]),x=f[x][i];
}
void Getlca(int x,int y)
{
	p.a=p.b=-inf;
	if (d[x]>d[y]) swap(x,y);
	Move(y,d[x]);
	if (x==y) return;
	for (int i=20;i>=0;i--)
		if (f[x][i]!=f[y][i])
		{
			Update(p,g[x][i]),Update(p,g[y][i]);
			x=f[x][i],y=f[y][i];
		}
	Update(p,g[x][0]),Update(p,g[y][0]);
}
int main()
{
    scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
		scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].v);
	for (int i=1;i<=n;i++)
		fa[i]=i;
	Kruscal();
	Build();
	ST();
	LL ans=(LL)1e15;
	for (int i=1;i<=m;i++)
		if (!v[i])
		{
			Getlca(E[i].x,E[i].y);
			if (p.a==E[i].v)
				ans=min(ans,pre+E[i].v-p.b);
			else ans=min(ans,pre+E[i].v-p.a);
		}
	printf("%lld\n",ans);
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值