bzoj1977次小生成树

本文介绍了一道复杂的倍增算法题目,通过实现倍增算法来解决寻找路径中的最大值和第二大值的问题。文章详细展示了如何利用并查集找到最小生成树,并在此基础上进行倍增预处理以高效地求解LCA问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一道复杂一点的倍增题,需要记录路径的最大值和第二大值。

注意,严格次小生成树,等生成树性质要明确

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll ans,f=1;char ch;
    while ((ch=getchar())<'0'||ch>'9') if (ch=='-') f=-1;ans=ch-'0';
    while ((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0';
    return ans*f;
}
 
 
int n,m,fa[120005];
struct aa
{
    int u,v;
    ll dis;
    bool o;
    bool operator <(const aa &b)const
    {
        return dis<b.dis;
    }
}bian[320005];
int find(int i) { return fa[i]==i?i:fa[i]=find(fa[i]);}
 
 
 
int head[120005],tot;
struct aaa
{
    int to,pre;
    ll dis;
}edge[230005];
void addedge(int u,int v,ll dis)
{
    edge[++tot].to=v;edge[tot].pre=head[u];edge[tot].dis=dis;head[u]=tot;
}


int f[120005][20],dep[120005];ll g1[120005][20],g2[120005][20];

ll se(ll a,ll b,ll c,ll d)
{
	int j;ll t[4]={a,b,c,d};
	sort(t,t+4);
	for (j=3;j>=0;j--) if (t[j]!=t[3]) break;
	if (j==-1) return 0;
	return t[j];
}

void dfs(int u,int depth)
{
    dep[u]=depth;
    for (int i=1;i<=18;i++)
    {
    	int ff=f[u][i-1];
    	f[u][i]=f[ff][i-1];
    	g1[u][i]=max(g1[u][i-1],g1[ff][i-1]);
    	g2[u][i]=se(g1[u][i-1],g1[ff][i-1],g2[u][i-1],g2[ff][i-1]);
	}
    for (int i=head[u];i;i=edge[i].pre)
    if (dep[edge[i].to]==0)
    {
        int v=edge[i].to;
        f[v][0]=u,g1[v][0]=edge[i].dis,g2[v][0]=0;
        dfs(v,depth+1);
    }
}

void updata(ll &fi,ll &se,ll a)
{
	if (a>fi) se=fi,fi=a;
	else if (a==fi) return;
	else if (a>se) se=a;
}

void up(int &u,ll &fi,ll &se,int step)
{
    for (int i=0;i<=18;i++) 
	if (step&(1<<i))
	{
		updata(fi,se,g1[u][i]);updata(fi,se,g2[u][i]);
		u=f[u][i];
	}
}

ll lca(int u,int v,ll dis)
{
    ll se=0,fi=0;
    if (dep[u]>dep[v]) up(u,fi,se,dep[u]-dep[v]);
    else up(v,fi,se,dep[v]-dep[u]);
    
    for (int i=18;i>=0;i--) 
    if (f[u][i]!=f[v][i])
    {
		updata(fi,se,g1[u][i]);updata(fi,se,g1[v][i]);
		updata(fi,se,g2[u][i]);updata(fi,se,g2[v][i]);
        u=f[u][i],v=f[v][i];
    }
    if (u!=v) updata(fi,se,g1[u][0]),updata(fi,se,g1[v][0]);
    if (dis>fi) return fi;else return se;
}
int main()
{
    n=read(),m=read();
    int x,y;ll z;
    for (int i=1;i<=m;i++) bian[i].u=read(),bian[i].v=read(),bian[i].dis=read();
    sort(bian+1,bian+m+1);
    for (int i=1;i<=n;i++) fa[i]=i;
    ll sum=0,ans=1e16;
    for (int i=1;i<=m;i++)
    {
        int fu=find(bian[i].u),fv=find(bian[i].v);
        if (fu!=fv)
        {
            fa[fu]=fv;
            sum+=bian[i].dis;
            bian[i].o=true;
            addedge(bian[i].u,bian[i].v,bian[i].dis);
            addedge(bian[i].v,bian[i].u,bian[i].dis);
        }
    }
    
    dfs(1,1);
    for (int i=1;i<=m;i++)
    if (!bian[i].o)
    {
        ll mx=lca(bian[i].u,bian[i].v,bian[i].dis);
		if (mx!=0) ans=min(sum-mx+bian[i].dis,ans);
    }
    printf("%lld",ans);
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值