bzoj 1977: [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

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

        刚学了非严格次小生成树,跑来打一波严格次小生成树,交上去发现noip前竟然做过这题...

        首先呢有意思的是严格次小生成树与非严格次小生成树都是只与最小生成树只有一条边的差距,证明方法与非严格次小生成树的证明相似。

        知道了这个结论了求严格次小生成树就比较简单了,如求次短路一样,处理maxx[i][26],maxx2[i][26],两个数组代表点i向上2的k次方步的最大与严格次大值,如果最大由两个不一样的最大递推而来,那么次大可选两个最大的小的那一个,也可以是由两个次大的大者递推而来。

        在处理lca的时候,如果最大值被更新了,次大值就会是当前次大值与之前的最大值的最大值,如果最大值没有被当前的maxx[i][k]更新,那么次大值也有可能是maxx[i][k]与当前次大值的较大者,当然次大值本身也是当前向上k次方步的次大值与原来次大值的较大者。

         下附AC代码。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define maxn 300005
using namespace std;
typedef long long ll;
ll n,m;
ll cnt,tot;
ll vis[maxn],dep[maxn];
ll anc[maxn][26],maxx[maxn][26],maxx2[maxn][26];
ll head[2*maxn],ne[2*maxn],to[2*maxn],val[2*maxn];
void add(ll x,ll y,ll w)
{
	to[++tot]=y;val[tot]=w;ne[tot]=head[x];head[x]=tot;
}
//以下为最小生成树 
ll fa[maxn];
ll u[maxn],e[maxn],w[maxn],temp[maxn];
ll cmp(ll i,ll j)
{
	return w[i]<w[j];
}
ll getfa(ll now)
{
	return fa[now]==now ? now : fa[now]=getfa(fa[now]);
}
ll kruskal()
{
	sort(temp+1,temp+1+m,cmp);
	for(ll i=1;i<=n;i++)
	fa[i]=i;
	ll ans=0;
	for(ll i=1;i<=m;i++)
	{
		ll now=temp[i];
		ll x1=getfa(u[now]);
		ll y1=getfa(e[now]);
		if(x1!=y1)
		{
			vis[temp[i]]=1;
			fa[x1]=y1;
			add(e[now],u[now],w[now]);
			add(u[now],e[now],w[now]);
			ans+=w[now];
			cnt++;
		}
	} 
	return cnt==n-1 ? ans : -1;
}
//以下为处理lca 
void dfs(ll now,ll pa)
{
	for(ll i=1;i<=24;i++)
	{
		if((1<<i)>dep[now]) break;
		anc[now][i]=anc[anc[now][i-1]][i-1];
		maxx[now][i]=max(maxx[now][i-1],maxx[anc[now][i-1]][i-1]);
		if(maxx[now][i-1]==maxx[anc[now][i-1]][i-1])
		{
			maxx2[now][i]=max(maxx2[now][i-1],maxx2[anc[now][i-1]][i-1]);
		}
		else
		{
			maxx2[now][i]=max(maxx2[now][i-1],maxx2[anc[now][i-1]][i-1]);
			maxx2[now][i]=max(maxx2[now][i],min(maxx[now][i-1],maxx[anc[now][i-1]][i-1]));
		}
	}
	for(ll i=head[now];i;i=ne[i])
	{
		ll nex=to[i];
		if(nex!=pa)
		{
			dep[nex]=dep[now]+1;
			anc[nex][0]=now;
			maxx[nex][0]=val[i];
			dfs(nex,now);
		}
	}
}
pair<ll,ll> lca(ll p,ll q)
{
	ll ans1=0,ans2=0;
	if(dep[p]<dep[q]) swap(p,q);
	ll d=dep[p]-dep[q];
	for(ll i=0;i<=24;i++)
	{
		if((1<<i)&d)
		{
			if(maxx[p][i]>ans1) 
			{
				ans2=max(ans2,ans1);
				ans1=maxx[p][i];
			}
			ans2=max(ans2,maxx2[p][i]);
			p=anc[p][i];
		}
	}
	if(p==q) return make_pair(ans1,ans2);
	for(ll i=24;i>=0;i--)
	{
		if(anc[p][i]!=anc[q][i])
		{
			if(maxx[p][i]>ans1) ans2=max(ans2,ans1),ans1=maxx[p][i];
			else if(maxx[p][i]<ans1) ans2=max(ans2,maxx[p][i]);
			ans2=max(ans2,maxx2[p][i]);	p=anc[p][i];
			if(maxx[q][i]>ans1) ans2=max(ans2,ans1),ans1=maxx[q][i];
			else if(maxx[q][i]<ans1) ans2=max(ans2,maxx[q][i]);
			ans2=max(ans2,maxx2[q][i]);	q=anc[q][i];
		}
	}
	if(maxx[p][0]>ans1) ans2=max(ans2,ans1),ans1=maxx[p][0];
	else if(maxx[p][0]<ans1) ans2=max(ans2,maxx[p][0]);
	ans2=max(ans2,maxx2[p][0]);
	if(maxx[q][0]>ans1) ans2=max(ans2,ans1),ans1=maxx[q][0];
	else if(maxx[q][0]<ans1) ans2=max(ans2,maxx[q][0]);
	ans2=max(ans2,maxx2[q][0]);
	return make_pair(ans1,ans2);
}
int main()
{
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&u[i],&e[i],&w[i]);
		temp[i]=i;
	}
	ll ans=kruskal();
	dfs(1,1);
	ll cha=987654321;
	for(ll i=1;i<=m;i++)
	if(!vis[i])
	{
		ll x=u[i],y=e[i];
		pair<ll,ll> now=lca(x,y);
		if(now.first==w[i])
		cha=min(cha,w[i]-now.second);
		else
		cha=min(cha,w[i]-now.first);
	}
	printf("%lld\n",ans+cha);
}


 



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值