有定理
存在次小生成树,只替换最小生成树的一条边得来。
那么先跑一遍最小生成树
然后枚举不在树边的边 ( u , v ) (u,v) (u,v)
假如加入这条边,树就会成环,我们需要删掉 u − > v u->v u−>v的一条树边
显然删掉最大的那条边最优秀
这个过程使用 L C A LCA LCA来优化
带权 L C A LCA LCA
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int inf=1e18;
int n,m;
int pre[maxn],deep[maxn],lg[maxn],ok[maxn];
int fa[maxn][21],maxx[maxn][21],ci[maxn][21];
struct p{
int l,r,w;
bool operator < (const p&tmp ){
return w<tmp.w;
}
}a[maxn<<2];
struct edge{
int to,nxt,w;
}d[maxn<<2]; int head[maxn<<2],cnt=1;
void add(int u,int v,int w){d[++cnt] = (edge){v,head[u],w},head[u]=cnt;}
int find(int x){ return x==pre[x]?x:pre[x]=find(pre[x]);}
int kur()
{
sort(a+1,a+1+m);
for(int i=1;i<=n;i++) pre[i]=i;
int ans=0;
for(int i=1,j=1;i<n;i++)
for(;j<=m;j++)
{
int fl=find(a[j].l),fr=find(a[j].r),w=a[j].w;
if( fl!=fr )
{
add(a[j].l,a[j].r,w); add(a[j].r,a[j].l,w);
pre[fl]=fr, ans+=w; ok[j]=1;
break;
}
}
return ans;
}
void dfs(int u,int father)
{
deep[u]=deep[father]+1,fa[u][0]=father;
for(int i=1;i<=lg[deep[u]]+1;i++)
{
int r = fa[u][i-1];
fa[u][i]=fa[r][i-1];
maxx[u][i]=max( maxx[u][i-1],maxx[r][i-1] );
ci[u][i]=max( ci[u][i],ci[r][i-1] );
if( maxx[u][i-1]>maxx[r][i-1] )
ci[u][i] = max( ci[u][i],maxx[r][i-1] );
else if( maxx[u][i-1]<maxx[r][i-1] )
ci[u][i] = max( ci[u][i],maxx[u][i-1] );
}
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( v==father ) continue;
maxx[v][0]=d[i].w;
ci[v][0]=-inf;
dfs(v,u);
}
}
int LCA(int x,int y,int w)
{
int ans = -inf;
if( deep[x]<deep[y] ) swap(x,y);
for(int i=20;i>=0;i--)
if( deep[fa[x][i]]>=deep[y] )
{
if( w!=maxx[x][i] ) ans = max(ans,maxx[x][i] );
else ans = max(ans,ci[x][i] );
x = fa[x][i];
}
if( x==y ) return ans;
for(int i=20;i>=0;i--)
if( fa[x][i]!=fa[y][i] )
{
if( w!=maxx[x][i] ) ans = max(ans,maxx[x][i] );
else ans = max(ans,ci[x][i] );
if( w!=maxx[y][i] ) ans = max(ans,maxx[y][i] );
else ans = max(ans,ci[y][i] );
x=fa[x][i],y=fa[y][i];
}
if( w!=maxx[x][0] ) ans = max(ans,maxx[x][0] );
else ans = max(ans,ci[x][0] );
if( w!=maxx[y][0] ) ans = max(ans,maxx[y][0] );
else ans = max(ans,ci[y][0] );
return ans;
}
void init()
{
for(int i=1;i<=n;i++)
lg[i] = lg[i-1]+((1<<lg[i-1])==i);
}
signed main()
{
cin >> n >> m;
init();
for(int i=1;i<=m;i++)
cin >> a[i].l >> a[i].r >> a[i].w;
int chu = kur();
ci[1][0]=-inf;
dfs(1,0);
int ans = inf;
for(int i=1;i<=m;i++)
{
if( ok[i] ) continue;
int u = a[i].l,v = a[i].r, w = a[i].w;
ans = min( ans,chu-LCA(u,v,w)+w );
}
cout << ans;
}
先求 l c a lca lca再跳,其实都差不多
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int inf=1e18;
int n,m;
int pre[maxn],deep[maxn],lg[maxn],ok[maxn];
int fa[maxn][21],maxx[maxn][21],ci[maxn][21];
struct p{
int l,r,w;
bool operator < (const p&tmp ){
return w<tmp.w;
}
}a[maxn<<2];
struct edge{
int to,nxt,w;
}d[maxn<<2]; int head[maxn<<2],cnt=1;
void add(int u,int v,int w){d[++cnt] = (edge){v,head[u],w},head[u]=cnt;}
int find(int x){ return x==pre[x]?x:pre[x]=find(pre[x]);}
int kur()
{
sort(a+1,a+1+m);
for(int i=1;i<=n;i++) pre[i]=i;
int ans=0;
for(int i=1,j=1;i<n;i++)
for(;j<=m;j++)
{
int fl=find(a[j].l),fr=find(a[j].r),w=a[j].w;
if( fl!=fr )
{
add(a[j].l,a[j].r,w); add(a[j].r,a[j].l,w);
pre[fl]=fr, ans+=w; ok[j]=1;
break;
}
}
return ans;
}
void dfs(int u,int father)
{
deep[u]=deep[father]+1,fa[u][0]=father;
for(int i=1;i<=lg[deep[u]];i++)
{
int r = fa[u][i-1];
fa[u][i]=fa[r][i-1];
maxx[u][i]=max( maxx[u][i-1],maxx[r][i-1] );
ci[u][i]=max( ci[u][i],ci[r][i-1] );
if( maxx[u][i-1]>maxx[r][i-1] )
ci[u][i] = max( ci[u][i],maxx[r][i-1] );
else if( maxx[u][i-1]<maxx[r][i-1] )
ci[u][i] = max( ci[u][i],maxx[u][i-1] );
}
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( v==father ) continue;
maxx[v][0]=d[i].w;
ci[v][0]=-inf;
dfs(v,u);
}
}
int LCA(int x,int y)
{
if( deep[x]<deep[y] ) swap(x,y);
for(int i=20;i>=0;i--)
if( deep[fa[x][i]]>=deep[y] ) x=fa[x][i];
if( x==y ) return x;
for(int i=20;i>=0;i--)
if( fa[x][i]!=fa[y][i] )
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int make_max(int x,int y,int da)
{
int ans = -inf;
for(int i=20;i>=0;i--)
if( deep[fa[x][i]]>=deep[y] )
{
if( da!=maxx[x][i] ) ans =max( ans,maxx[x][i] );
else ans = max( ans,ci[x][i] );
x = fa[x][i];
}
return ans;
}
void init()
{
for(int i=1;i<=n;i++)
lg[i] = lg[i-1]+((1<<lg[i-1])==i);
}
signed main()
{
cin >> n >> m;
init();
for(int i=1;i<=m;i++)
cin >> a[i].l >> a[i].r >> a[i].w;
int chu = kur();
ci[1][0]=-inf;
dfs(1,0);
int ans = inf;
for(int i=1;i<=m;i++)
{
if( ok[i] ) continue;
int u = a[i].l,v = a[i].r, w = a[i].w;
int lca = LCA(u,v);
int q=make_max(u,lca,w), e = make_max(v,lca,w);
ans = min( ans,chu-max(q,e)+w );
}
cout << ans;
}
非严格次小生成树
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 2e5+10;
const int inf = 1e9;
struct p{
int l,r,w;
bool operator < (const p&tmp ) const{
return w<tmp.w;
}
}a[maxn];
struct edge{
int to,nxt,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int w){
d[++cnt] = (edge){v,head[u],w},head[u] = cnt;
}
int fa[maxn][22],deep[maxn],maxx[maxn][22],pre[maxn],n,m,ok[maxn];
int find(int x){ return x==pre[x]?x:pre[x]=find(pre[x]);}
int kur()
{
int ans = 0;
for(int i=1;i<=n;i++) pre[i] = i;
sort( a+1,a+1+m );
for(int i=1;i<=m;i++)
{
int fl = find(a[i].l), fr = find(a[i].r);
if( fl==fr ) continue;
pre[fl] = fr; ok[i] = 1;
add( a[i].l,a[i].r,a[i].w ); add( a[i].r,a[i].l,a[i].w );
ans += a[i].w;
}
return ans;
}
void dfs(int u,int father)
{
fa[u][0]=father, deep[u] = deep[father]+1;
for(int i=1;i<=20;i++)
{
int r = fa[u][i-1];
fa[u][i] = fa[r][i-1];
maxx[u][i] = max( maxx[u][i-1],maxx[r][i-1] );
}
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( v==father ) continue;
maxx[v][0] = d[i].w;
dfs(v,u);
}
}
int lca(int x,int y)
{
if( deep[x]<deep[y] ) swap(x,y);
int ans = -inf;
for(int i=20;i>=0;i--)
if( deep[fa[x][i]]>=deep[y] )
{
ans = max( ans,maxx[x][i] );
x = fa[x][i];
}
if( x==y ) return ans;
for(int i=20;i>=0;i--)
if( fa[x][i]!=fa[y][i] )
{
ans = max( ans,max(maxx[x][i],maxx[y][i]) );
x = fa[x][i], y = fa[y][i];
}
ans = max( ans,max( maxx[x][0],maxx[y][0]) );
return ans;
}
void init()
{
for(int i=1;i<=m;i++) ok[i] = 0;
cnt = 1;
for(int i=1;i<=n;i++) head[i] = 0;
}
int main()
{
int T; cin >> T;
while( T-- )
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
cin >> a[i].l >> a[i].r >> a[i].w;
int chu = kur(),ans = inf;
dfs(1,0);
for(int i=1;i<=m;i++)
{
if( ok[i] ) continue;
ans = min( ans , chu-lca(a[i].l,a[i].r)+a[i].w );
}
// if( chu==ans ) cout << "Not Unique!\n";
// else cout << chu << endl;
init();
}
}