世界真的很大
最小生成树的算法都知道了
但是最小生成树的原理和方法还是需要详细了解的,因为就如这道题一样会有很多变种
比如之前这道题:CodeForces 827D
最小生成树选边的原理要牢记于心
看题先:
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
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
首先分析题意
由于当时几乎是秒想题解的,现在总结起来反而有些困难233
考虑由于是次小生成树,离最小生成树是非常接近的,所以他们的结构几乎差不多
就是用一条稍微大一点的边去替代最小生成树上的某条边
具体来说,就是对于所有没有被选到的边,其连接的两点在最小生成树上的链上和这条边的权值相减的最小值
由于是最小生成树,所以这条链上的所有边权都会比这条边要小,所以我们只需要在最小生成树的这条链上找一个最大值就行了
这一步我们可以用lca跳倍增时顺便维护
特别的,如果这条链上的最大值正好等于这条边的权值,由于是严格次小,而不是最小生成树的另一种结构,所以这样是不合法的
于是乎我们在维护最大值时还需要维护链上的次大值,如果最大值与边的边权相等,就用次大值去相减
完整代码:
#include<stdio.h>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long dnt;
const int INF=0x3f3f3f3f;
struct edge
{
int u,v,w,last;
}ed[2000010],sid[2000010];
int n,m,num=0,tot=0,ans=INF;
int head[100010],fa[100010],dep[100010];
int anc[100010][20],st[100010][20],sd[100010][20],used[300010];
dnt bns=0;
bool cmp(const edge &a,const edge &b)
{
return a.w<b.w;
}
void add(int u,int v,int w)
{
num++;
ed[num].v=v;
ed[num].w=w;
ed[num].last=head[u];
head[u]=num;
}
int getfather(int x)
{
if(x==fa[x]) return x;
return fa[x]=getfather(fa[x]);
}
void Kruscal()
{
sort(sid+1,sid+m+1,cmp);
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
int x=getfather(sid[i].u),y=getfather(sid[i].v);
if(x!=y)
{
used[i]=1;
tot++,bns+=sid[i].w;
add(sid[i].u,sid[i].v,sid[i].w);
add(sid[i].v,sid[i].u,sid[i].w);
fa[x]=y;
}
if(tot==n-1) return ;
}
return ;
}
void dfs(int u,int f)
{
anc[u][0]=f;
for(int p=1;p<=16;p++)
{
anc[u][p]=anc[anc[u][p-1]][p-1],
st[u][p]=max(st[u][p-1],st[anc[u][p-1]][p-1]);
if(st[u][p-1]==st[anc[u][p-1]][p-1])
sd[u][p]=max(sd[u][p-1],sd[anc[u][p-1]][p-1]);
else
{
sd[u][p]=min(st[u][p-1],st[anc[u][p-1]][p-1]);
sd[u][p]=max(sd[u][p-1],sd[u][p]);
sd[u][p]=max(sd[u][p],sd[anc[u][p-1]][p-1]);
}
}
for(int i=head[u];i;i=ed[i].last)
{
int v=ed[i].v;
if(v==f) continue ;
dep[v]=dep[u]+1;
st[v][0]=ed[i].w;
dfs(v,u);
}
}
void calmx(int wth,int vth,int &d,int &e)
{
if(wth==d) return ;
if(wth>d) e=d,d=wth;
else if(wth>e) e=wth;
e=max(e,vth);
}
int lca(int u,int v,int xx)
{
int d=0,e=0;
if(dep[u]<dep[v]) swap(u,v);
for(int i=16;i>=0;i--)
if(dep[anc[u][i]]>=dep[v])
{
calmx(st[u][i],sd[u][i],d,e);
u=anc[u][i];
}
if(u==v)
{
if(xx==d) return e;
return d;
}
for(int i=16;i>=0;i--)
if(anc[u][i]!=anc[v][i])
{
calmx(st[u][i],sd[u][i],d,e);calmx(st[v][i],sd[u][i],d,e);
u=anc[u][i],v=anc[v][i];
}
calmx(st[u][0],sd[u][0],d,e),calmx(st[v][0],sd[v][0],d,e);
if(xx==d) return e;
return d;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&sid[i].u,&sid[i].v,&sid[i].w);
Kruscal();
dfs(1,1);
for(int i=1;i<=m;i++)
{
if(used[i]) continue ;
int u=sid[i].u,v=sid[i].v;
int tmp=lca(u,v,sid[i].w);
ans=min(ans,sid[i].w-tmp);
}
cout << ans+bns <<endl ;
return 0;
}
/*
EL PSY CONGROO
*/
嗯,就是这样