ACM Contest and Blackout(非严格次小生成树板子+LCA)

11 篇文章 0 订阅
6 篇文章 0 订阅

https://vjudge.net/problem/UVA-10600/origin


板子题。

求非严格次小生成树树有大致有两种。一种是暴力,枚举不在最小生成树的边,然后跑n-1次最小生成树。(O(n^2logn)大致)

或者dfs找最小生成树环上的最长边替换掉。

xayata_大佬的图。

优化的办法就是在最小生成树中,看成一颗树,然后化成有根树,用倍增的思路融合ST的思路,维护u,v的lca(u,v)的最长边。

一个倍增数组记录父亲,一个倍增数组记录(u,lca(u,v))之间的最长边。

然后枚举。大致是O(nlogn)

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=150;
typedef int LL;
LL n,m;
struct P{
	LL u,v,c;
	LL num;
}edge[maxn*maxn];
vector<P>g[maxn];
LL p[maxn];
LL bz[maxn][20],maxi[maxn][20],deep[maxn];
bool used[maxn*maxn],vis[maxn*maxn];
bool cmp(P A,P B)
{
	return A.c<B.c;
}
LL find(LL x)
{
	if(p[x]==x) return p[x];
	return p[x]=find(p[x]);
}
void init()
{
	for(LL i=1;i<110;i++) p[i]=i,g[i].clear();
	memset(vis,0,sizeof(vis));
	memset(used,0,sizeof(used));
	memset(deep,0,sizeof(deep));
	memset(edge,0,sizeof(edge));
	memset(bz,0,sizeof(bz));
	memset(maxi,0,sizeof(maxi));
}
void dfs(LL u,LL fa)
{

	bz[u][0]=fa;///cout<<"fuck"<<endl;
	for(LL i=0;i<g[u].size();i++){
		LL v=g[u][i].v;
		if(v==fa||vis[v]) continue;
		vis[v]=true;
		deep[v]=deep[u]+1;
		maxi[v][0]=edge[g[u][i].num].c;
		dfs(v,u);
	}
}
void cal()
{
	for(LL i=1;i<=18;i++)
	{
		for(LL j=1;j<=n;j++)
		{
			bz[j][i]=bz[bz[j][i-1]][i-1];
			maxi[j][i]=max(maxi[j][i-1],maxi[bz[j][i-1]][i-1]);
		}
	}
}
LL LCA(LL x,LL y)
{
	if(deep[x]<deep[y]) swap(x,y);
	for(LL i=18;i>=0;i--){
		if(deep[bz[x][i]]>=deep[y])
			x=bz[x][i];
	}
	if(x==y) return x;
	for(LL i=18;i>=0;i--){
		if(bz[x][i]!=bz[y][i]){
			x=bz[x][i];y=bz[y][i];
		}
	}
	return bz[x][0];
}
LL qmax(LL u,LL v,LL maxx)
{
	LL res=-0x3f3f3f3f;
	for(LL i=18;i>=0;i--)
	{
		if(deep[bz[u][i]]>=deep[v])
		{
			res=max(res,maxi[u][i]);
			u=bz[u][i];
		}
	}
	return res;
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL t;cin>>t;
  while(t--)
  {
  	init();
  	cin>>n>>m;
  	for(LL i=1;i<=m;i++){
  		LL u,v,c;cin>>u>>v>>c;
		edge[i].u=u;edge[i].v=v;edge[i].c=c;		
	}
	sort(edge+1,edge+1+m,cmp);
	LL ans=0;//最小生成树 
	for(LL i=1;i<=m;i++)
	{
		LL u=edge[i].u;LL v=edge[i].v;LL c=edge[i].c;
		if(find(u)==find(v)) continue;
		p[find(u)]=find(v);
	///	cout<<edge[i].u<<"---"<<edge[i].v<<endl;
		g[u].push_back({u,v,c,i});
		g[v].push_back({v,u,c,i});
		
		used[i]=true;
		ans+=edge[i].c;
	}	
	deep[1]=1;vis[1]=true;
	dfs(1,-1);
	cal();
	LL ans2=0x3f3f3f3f;
	for(LL i=1;i<=m;i++)
	{
		if(used[i]) continue;
		LL u=edge[i].u;
		LL v=edge[i].v;
		LL c=edge[i].c;
		LL lca=LCA(u,v);
		LL maxu=qmax(u,lca,c);
		LL maxv=qmax(v,lca,c);	
		ans2=min(ans2,ans-max(maxu,maxv)+c);
	}
	cout<<ans<<" "<<ans2<<endl;
  }
return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值