【题解】严格次小生成树
题目大意:求一个严格的次小生成树,使得其边权和严格小于最小生成树。
(本来想着\(LCT\)但笔者太菜)
我们可以试着想一个暴力思路:枚举每一条加进去的边,看看替换怎么样,记录下每一次替换后的生成树边权和,然后比较答案。
先跑一边最小生成树。
考虑我们替掉树上的哪一条边。
因为我们加进去的应该是那条边所在环上的最大的边,我们替的话应该\(instead\) \(of\) 最大的边。
树上维护最大边权,想到了啥?
LCT
树上倍增。
我们可以倍增思路,预处理出来点\(i\)到它的\(2^{j}\)次方父亲的路径中的最大值。
那么我们找树上两点最大值的时间复杂度就变成了\(\log(n)\)级别。
那么我们的代码复杂度也就是约\(O(n\log n)\)
可以通过本题。
注意:连边的时候是两次,所以排序的时候要排\((m+m)\)条边,倍增处理的时候\((1<<31)\)会爆\(int\),以及注意初始\(dfs\)的传参……
最大值递推式:
\(fm[i][j]=max(fm[i][j-1],fm[fa[i][j-1]][j-1])\)
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<1)+(s<<3)+(ch^48);
ch=getchar();
}
return s*w;
}
const int MAXN=5e5+10;
const int inf=(1LL<<60);
int H[MAXN<<1],tot,head[MAXN],cnt;
int n,m,fa[MAXN][31],fm[MAXN][31];
int f[MAXN],vis[MAXN],MAY,sum,ans;
int dep[MAXN];
struct edge{
int nxt,to,dis,pre;
}g[MAXN<<1],e[MAXN];
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline void add(int x,int y,int dis){
e[++tot].to=y;
e[tot].nxt=head[x];
e[tot].dis=dis;
e[tot].pre=x;
head[x]=tot;
}
inline void kdd(int x,int y,int dis){
g[++cnt].to=y;
g[cnt].nxt=H[x];
g[cnt].dis=dis;
g[cnt].pre=x;
H[x]=cnt;
}
inline bool cmp(edge a,edge b){return a.dis<b.dis;}
void kruskal(){
sort(g+1,g+m+m+1,cmp);
for(int i=1;i<=2*m;++i){
int x=g[i].pre,y=g[i].to;
int fx=find(x),fy=find(y);
if(fx==fy)continue;add(y,x,g[i].dis);sum+=g[i].dis;
f[fx]=fy;add(x,y,g[i].dis);vis[i]=1;vis[i+1]=1;
}
}
void dfs(int x,int father,int L){
fa[x][0]=father;
fm[x][0]=L;
dep[x]=dep[father]+1;
for(int i=1;(1<<i)<=dep[x];++i){
fa[x][i]=fa[fa[x][i-1]][i-1];
fm[x][i]=max(fm[x][i-1],fm[fa[x][i-1]][i-1]);
}
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(j==father)continue;
dfs(j,x,e[i].dis);
}
}
void LCA(int x,int y,int &SS,int val){
int res=-1e18;
if(dep[x]<dep[y])swap(x,y);
int X=x,Y=y;
int cha=dep[x]-dep[y];
for(int i=30;i>=0;--i)
if(cha>=(1<<i)){
cha-=(1<<i);
if(fm[x][i]!=val)res=max(res,fm[x][i]);
x=fa[x][i];
}
if(x==y){
SS-=res;SS+=val;
return;
}
for(int i=30;i>=0;--i)
if(fa[x][i]!=fa[y][i]){
if(fm[x][i]!=val)res=max(res,fm[x][i]);
if(fm[y][i]!=val)res=max(res,fm[y][i]);
x=fa[x][i],y=fa[y][i];
}
if(fm[x][0]!=val)res=max(res,fm[x][0]);
if(fm[y][0]!=val)res=max(res,fm[y][0]);
SS-=res;SS+=val;
}
void solve(){
ans=inf;
int s=0;
for(int i=1;i<=m+m;++i){
if(!vis[i]){
int x=g[i].pre,y=g[i].to;
LCA(x,y,(s=sum),g[i].dis);
ans=min(ans,s);
vis[i+1]=1;
}
}
printf("%lld\n",ans);
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;++i)f[i]=i;
for(int i=1;i<=m;++i){
int x,y,z;
x=read(),y=read(),z=read();
kdd(x,y,z);kdd(y,x,z);
}
kruskal();
dfs(1,0,0);
solve();
return 0;
}
注意开\(long\) \(long\).
\(Over.\)