标签:更新 节点 == line space int 最小生成树 logs scan
首先要想清楚一个问题,对于次小生成树,肯定是在最小生成树上断掉一条边,然后在非最小生成树边中加一条边进去产生的(具体窝也不会证鸭;知道了这个非常显然的结论后具体思路就好想了.
我们考虑暴力删边和加边.对于每条未在最小生成树中的边,考虑删掉最小生成树中的一条边(边权尽可能大)并加入此边,因为非最小生成树的边权肯定都大于等于最小生成树边的边权,所以删掉的边边权越大,新树的总边权才会越小,并且要保证新树还是生成树.
所以我们先求出最小生成树记录下总边权值,然后对于每一条非最小生成树边的两个节点在最小生成树上求一次\(lca\)(保证新树是生成树),然后求出节点到\(lca\)的最大边权和次大边权(因为最大边权可能和要加进去的边边权相等不满足严格次小生成树,所以要多求一个次小边权).然后用总边权减去求得最大边权(或次大边权)再加上要加进去的边的边权,记录答案最小值即可.
// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=100000+5;
const int M=300000+5;
const int logs=19;
const int INF=2147483647*1e9;
int n,m,tot,cnt,sum;
int head[N],dep[N],pre[N][logs+1],vis[M],fa[N],zd[N][logs+1],cd[N][logs+1];
struct Edge{
int next,to,dis;
}e[M*2];
struct node{
int u,v,w;
}a[M*2];
int find(int x){return x==fa[x] ? fa[x] : fa[x]=find(fa[x]);}
inline void merge(int x,int y){fa[x]=y;}
inline void add_edge(int from,int to,int dis){
e[++tot].next=head[from];
e[tot].to=to;
e[tot].dis=dis;
head[from]=tot;
}
bool cmp(node a,node b){return a.w<b.w;}
void dfs(int now,int faa){
pre[now][0]=faa;
dep[now]=dep[faa]+1;
for(int i=1;i<=logs;i++){
pre[now][i]=pre[pre[now][i-1]][i-1];
zd[now][i]=max(zd[now][i-1],zd[pre[now][i-1]][i-1]);//最大值
cd[now][i]=max(cd[now][i-1],cd[pre[now][i-1]][i-1]);//次大值
if(zd[now][i-1]>zd[pre[now][i-1]][i-1]) cd[now][i]=max(cd[now][i],zd[pre[now][i-1]][i-1]);//这里次大值可能与两个最大值中较小的那个更新
else if(zd[now][i-1]<zd[pre[now][i-1]][i-1]) cd[now][i]=max(cd[now][i],zd[now][i-1]);
}//倍增处理祖先,最大值,次大值.
for(int i=head[now];i;i=e[i].next)
if(e[i].to!=faa) dfs(e[i].to,now);
}
void cx(int x,int faa){
for(int i=head[x];i;i=e[i].next){
int v=e[i].to;
if(v==faa) continue;
zd[v][0]=e[i].dis;
cd[v][0]=-INF;
cx(v,x);
}
}//预处理最大值次大值数组
inline int lca(int x,int y,int w){
int ans=-INF;
if(dep[x]<dep[y]) std::swap(x,y);
for(int i=logs;i>=0;i--)
if(dep[pre[x][i]]>=dep[y]){
if(zd[x][i]==w) ans=max(ans,cd[x][i]);
else ans=max(ans,zd[x][i]);
x=pre[x][i];
}
if(x==y) return ans;
for(int i=logs;i>=0;i--)
if(pre[x][i]!=pre[y][i]){
if(w==zd[x][i]) ans=max(ans,cd[x][i]);
else ans=max(ans,zd[x][i]);
if(w==zd[y][i]) ans=max(ans,cd[y][i]);
else ans=max(ans,zd[y][i]);
x=pre[x][i],y=pre[y][i];
}
if(w==zd[x][0]) ans=max(ans,cd[x][0]);
else ans=max(ans,zd[x][0]);
if(w==zd[y][0]) ans=max(ans,cd[y][0]);
else ans=max(ans,zd[y][0]);
return ans;
}//这里直接在求lca的过程中把最大值和次大值求出来.
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++) scanf("%lld%lld%lld",&a[i].u,&a[i].v,&a[i].w);
sort(a+1,a+1+m,cmp);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
int x=find(a[i].u),y=find(a[i].v);
if(x!=y){
++cnt;
merge(x,y);
sum+=a[i].w;
add_edge(a[i].u,a[i].v,a[i].w);
add_edge(a[i].v,a[i].u,a[i].w);
vis[i]=1;//标记最小生成树边
if(cnt==n-1) break;
}
}//最小生成树
cx(1,0);
dfs(1,0);//预处理
int ans=INF;
for(int i=1;i<=m;i++){
if(!vis[i]){
int k=lca(a[i].u,a[i].v,a[i].w);
ans=min(ans,sum-k+a[i].w);
}
}
printf("%lld",ans);
return 0;
}
luogu P4180 【模板】严格次小生成树[BJWC2010]
标签:更新 节点 == line space int 最小生成树 logs scan
原文地址:https://www.cnblogs.com/cnyali-xwx/p/11323655.html