[BJWC2010] 严格次小生成树 - 洛谷
核心思路
求最近公共祖先、并维护最大值、次大值
表示节点i 向上走
步,的最大值
同理,维护次大值
更新公式
an[u][i] = max(an[u][i-1],an[f[u][i-1]][i-1]);
an1[u][i] = max(max(an1[u][i-1],an1[f[u][i-1]][i-1]),(an[u][i-1] == an[f[u][i-1]][i-1]?0:min(an[u][i-1],an[f[u][i-1]][i-1])));
查询公式
ans = max({ans,(w == an[u][i]?an1[u][i]:an[u][i]),(w == an[v][i]?an1[v][i]:an[v][i])});
升至同一高度
ans = max({ans,(w == an[u][i]?an1[u][i]:an[u][i]),(w == an[v][i]?an1[v][i]:an[v][i])});
同步上升
AC 代码
#include<bits/stdc++.h>
const int N = 1e6+9;
#define int long long
using namespace std;
int ans,n,m;
struct edge{
int u,v,w;
} e[N];
struct ede{
int v,w;
};
bool cmp(edge a,edge b){
return a.w < b.w;
}
vector<ede> g[N];
bool pd[N];
int fa[N],f[N][21],dep[N],sz[N],an[N][21],an1[N][21];
int find(int x){
if (x==fa[x]) return x;
else return fa[x]=find(fa[x]);
}
void krustral(){
sort(e+1,e+1+m,cmp);
for(int i = 1;i <= n;i++)fa[i] = i;
for(int i = 1;i <= m;i++){
int u = e[i].u,v = e[i].v,w = e[i].w;
if(find(u) != find(v)){
pd[i] = 1;
g[u].push_back({v,w});
g[v].push_back({u,w});
fa[find(u)] = find(v);
ans+=w;
}
}
}
void dfs(int u,int p,int w){
f[u][0] = p;
dep[u] = dep[p]+1;
//cout<<u<<" "<<dep[u]<<" o"<<endl;
an[u][0] = w,an1[u][0] = 0;
for(int i = 1;i <= 20;i++){
f[u][i] = f[f[u][i-1]][i-1];
an[u][i] = max(an[u][i-1],an[f[u][i-1]][i-1]);
an1[u][i] = max(max(an1[u][i-1],an1[f[u][i-1]][i-1]),(an[u][i-1] == an[f[u][i-1]][i-1]?0:min(an[u][i-1],an[f[u][i-1]][i-1])));
}
for(auto vis:g[u]){
int v = vis.v;
if(v == p)continue;
dfs(v,u,vis.w);
}
}
int lca(int u,int v,int w){
if(dep[u] < dep[v])swap(u,v);
int ans = 0;
if(u == v)return ans;
for(int i = 20;i >= 0;i--){
if(dep[f[u][i]] >= dep[v]){
// cout<<"入"<<" "<<u<<" "<<v<<endl;
ans = max({ans,an1[u][i],(w == an[u][i]?0:an[u][i])});
u = f[u][i];
}
}
if(u == v)return ans;
//cout<<"二阶段"<<" "<<ans<<endl;
for(int i = 20;i >= 0;i--){
if(f[u][i] != 0&&f[u][i] != f[v][i]){
ans = max({ans,(w == an[u][i]?an1[u][i]:an[u][i]),(w == an[v][i]?an1[v][i]:an[v][i])});
u = f[u][i],v = f[v][i];
}
}
return max({ans,(w == an[u][0]?an1[u][0]:an[u][0]),(w == an[v][0]?an1[v][0]:an[v][0])});
}
int pt = 1e18+7;
signed main(){
ios::sync_with_stdio(0);
cin>>n>>m;
for(int i = 1;i <= m;i++){
cin>>e[i].u>>e[i].v>>e[i].w;
}
krustral();
dfs(1,0,0);
// for(int i = 1;i <= n;i++){
// cout<<dep[i]<<"ok?"<<endl;
// }
//cout<<ans<<endl;
for(int i = 1;i <= m;i++){
if(!pd[i]){
int u = e[i].u,v = e[i].v,w = e[i].w;
// cout<<"P"<<u<<" "<<v<<" "<<w<<endl;
int z = lca(u,v,w);
if(z == 0){
continue;
}
if(ans-z+w > ans){
//cout<<ans+w-z<<"PP"<<endl;
pt = min(pt,ans-z+w);
}
}
}
cout<<pt<<endl;
return 0;
}
/*
次小生成树难寻,
AC严判更费神。
算法精妙须细究,
方得最优解一尘。
*/