【题解 P4180】严格次小生成树
[BJWC2010] 严格次小生成树
题目描述
小 C 最近学了很多最小生成树的算法,Prim 算法、Kruskal 算法、消圈算法等等。正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是 \(E_M\),严格次小生成树选择的边集是 \(E_S\),那么需要满足:(\(value(e)\) 表示边 \(e\) 的权值) \(\sum_{e \in E_M}value(e)<\sum_{e \in E_S}value(e)\)。
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
输入格式
第一行包含两个整数 \(N\) 和 \(M\),表示无向图的点数与边数。
接下来 \(M\) 行,每行 \(3\) 个数 \(x,y,z\) 表示,点 \(x\) 和点 \(y\) 之间有一条边,边的权值为 \(z\)。
输出格式
包含一行,仅一个数,表示严格次小生成树的边权和。
样例 #1
样例输入 #1
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
样例输出 #1
11
提示
数据中无向图不保证无自环。
对于 \(50\%\) 的数据, \(N\le 2000\),\(M\le 3000\)。
对于 \(80\%\) 的数据, \(N\le 5\times 10^4\),\(M\le 10^5\)。
对于 \(100\%\) 的数据, \(N\le 10^5\),\(M\le 3\times10^5\),边权 \(\in [0,10^9]\),数据保证必定存在严格次小生成树。
解法
题意还是很容易理解的,求严格最小的生成树。
很经典的一个想法,跑 \(kruskal\) 后最小生成树维护两点之间路径上最大的权值,枚举每条未放进最小生成树的边,拿最小生成树权值和减去路径最大权值再加上这条边的权值即可。
结果打完发现连样例都没过。
观察样例后发现,路径上存在与这条边权值相同的边,而题目是严格最小。
所以, \(LCA\) 时还要维护严格次小的边。
当路径上最大权值与此边权值相等时,就用次小权值来。
证明显然,只可能改掉一条边,因为原来是最小生成树,每次改边都会增大权值,改两边不如改一边优。
删掉最大或次大边,可以使剩下选的边权值和最小,从而使加上枚举的边后权值和最小。
代码
#include<bits/stdc++.h>
using namespace std;
struct datay
{
long long x,y,z;
}l[300005];
long long n,m,d[300005],f[300005][21],deep[300005],t[300005][21],val=0,t1[300005][21],val1;
bool v[300005];
vector<long long> a[300005],r[300005];
long long dijah(long long x)
{
if(d[x]!=x)d[x]=dijah(d[x]);
return d[x];
}
void dfs(long long x,long long y)
{
deep[x]=deep[y]+1;
f[x][0]=y;
for(int i=0;i<a[x].size();i++)
{
if(a[x][i]==y)continue;
t[a[x][i]][0]=r[x][i];
dfs(a[x][i],x);
}
return;
}
bool cmp(datay q,datay w)
{
return q.z<w.z;
}
long long LCA(long long x,long long y)
{
val=0;
val1=0;
if(deep[x]<deep[y])swap(x,y);
for(int i=20;i>=0;i--)
{
if(deep[f[x][i]]>=deep[y])
{
if(val1<=t1[x][i])
{
if(t1[x][i]>val)val1=t1[x][i];
else if(t[x][i]>val)val1=val;
else if(t[x][i]!=val)val1=t[x][i];
else val1=max(t1[x][i],val1);
}
else if(t1[x][i]<val1&&t[x][i]>=val1)val1=t[x][i]==val?val1:min(val,t[x][0]);
val=max(val,t[x][i]),x=f[x][i];
}
}
if(x==y)return x;
for(int i=20;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
if(val1<=t1[x][i])
{
if(t1[x][i]>val)val1=t1[x][i];
else if(t[x][i]>val)val1=val;
else if(t[x][i]!=val)val1=t[x][i];
else val1=max(t1[x][i],val1);
}
else if(t1[x][i]<val1&&t[x][i]>=val1)val1=t[x][i]==val?val1:min(val,t[x][0]);
val=max(val,t[x][i]);
if(val1<=t1[y][i])
{
if(t1[y][i]>val)val1=t1[y][i];
else if(t[y][i]>val)val1=val;
else if(t[y][i]!=val)val1=t[y][i];
else val1=max(t1[y][i],val1);
}
else if(t1[y][i]<val1&&t[y][i]>=val1)val1=t[y][0]==val?val1:min(val,t[y][0]);
val=max(val,t[y][i]);
x=f[x][i];
y=f[y][i];
}
}
if(val>t[x][0])val1=t[x][0];
val=max(val,t[x][0]);
if(val>t[y][0])val1=t[y][0];
val=max(val,t[y][0]);
return f[x][0];
}
int main()
{
long long x,y,s=0,minn=1e15+5;
memset(v,false,sizeof(v));
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&l[i].x,&l[i].y,&l[i].z);
}
for(int i=1;i<=m;i++)d[i]=i;
sort(l+1,l+m+1,cmp);
for(int i=1;i<=m;i++)
{
x=dijah(l[i].x),y=dijah(l[i].y);
if(x!=y)
{
a[l[i].x].push_back(l[i].y);
a[l[i].y].push_back(l[i].x);
r[l[i].x].push_back(l[i].z);
r[l[i].y].push_back(l[i].z);
d[y]=x;
s+=l[i].z;
v[i]=true;
}
}
dfs(1,0);
for(int i=1;i<=20;i++)
{
for(int j=1;j<=n;j++)
{
f[j][i]=f[f[j][i-1]][i-1];
t[j][i]=max(t[j][i-1],t[f[j][i-1]][i-1]);
if(t[j][i-1]!=t[f[j][i-1]][i-1])t1[j][i]=min(t[j][i-1],t[f[j][i-1]][i-1]);
else
{
t1[j][i]=max(t1[j][i-1],t1[f[j][i-1]][i-1]);
}
}
}
for(int i=1;i<=m;i++)
{
if(v[i]||l[i].x==l[i].y)continue;
LCA(l[i].x,l[i].y);
if(val!=l[i].z)minn=min(minn,s-val+l[i].z);
if(val1!=l[i].z)minn=min(minn,s-val1+l[i].z);
}
printf("%lld",minn);
return 0;
}