题目描述
原题来自:BeiJing 2010 组队赛
给定一张 NNN 个点 MMM 条边的无向图,求无向图的严格次小生成树。
设最小生成树的边权之和为 sum\text{sum}sum,严格次小生成树就是指边权之和大于 sum\text{sum}sum 的生成树中最小的一个。
输入格式
第一行包含两个整数 NNN 和 MMM,表示无向图的点数与边数;
接下来 MMM 行,每行三个数 x,y,zx,y ,zx,y,z,表示点 xxx 和点 yyy 之间有一条边,边的权值为 zzz。
输出格式
包含一行,仅一个数,表示严格次小生成树的边权和。
数据保证必定存在严格次小生成树。
样例
样例输入
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
样例输出
11
数据范围与提示
对于全部数据,1≤N≤105,1≤M≤3×1051\le N\le 10^5,1\le M\le 3\times 10^51≤N≤105,1≤M≤3×105,数据中无向图无自环,边权值非负且不超过 10910^9109。
懂最小生成树原理的人这题很好写,可以去网易公开课上看一下算法导论麻省理工的视频
先跑一遍最小生成树
然后再加入一条边,可以发现图中一定出现了环,
再删除其中原有的最大一条边就是答案之一了,用LCA维护即可
如果没有严格,那这题就KO了
如果有严格呢?
再记下次小的边
如果最小的边与加入的边权值一样就删去次小边
反之就删去最小边
细节自己想(我不会告诉你我一遍过的)
#include<cstdio>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
inline int read()
{
int ret=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',
ch=getchar();
return ret;
}
int n,m,cnt,num,t1,t2,ans;
ll s;
const int N=1e6+5;
int ff[N],he[N],to[N],nxt[N],w[N],dep[N];
int lg[N],f[N][21],d1[N][21],d2[N][21];
bool fl[N];
struct NA{
int u,v,w;
}e[N];
bool cmp(NA i,NA j)
{
return i.w<j.w;
}
int find(int x)
{
return ff[x]==x?x:ff[x]=find(ff[x]);
}
inline void add(int u,int v,int k)
{
to[++cnt]=v;
nxt[cnt]=he[u];
w[cnt]=k;
he[u]=cnt;
}
void dfs(int fa,int u)
{
dep[u]=(!fa)?0:dep[fa]+1;
f[u][0]=fa;
for(int i=1;i<=lg[dep[u]];i++)
{
f[u][i]=f[f[u][i-1]][i-1];
d1[u][i]=max(d1[u][i-1],d1[f[u][i-1]][i-1]);
d2[u][i]=max(d2[u][i-1],d2[f[u][i-1]][i-1]);
if(d1[u][i-1]!=d1[u][i])
d2[u][i]=max(d2[u][i],d1[u][i-1]);
if(d1[f[u][i-1]][i-1]!=d1[u][i])
d2[u][i]=max(d2[u][i],d1[f[u][i-1]][i-1]);
}
for(int e=he[u];e;e=nxt[e])
{
int v=to[e];
if(v!=fa)
d1[v][0]=w[e],
dfs(u,v);
}
}
void LCA(int u,int v)
{
if(dep[u]>dep[v]) swap(u,v);
t1=t2=0;
while(dep[u]<dep[v])
{
if(t1<d1[v][lg[dep[v]-dep[u]]])
t2=t1,t1=d1[v][lg[dep[v]-dep[u]]];
else if(t1!=d1[v][lg[dep[v]-dep[u]]])
t2=max(t2,d1[v][lg[dep[v]-dep[u]]]);
else t2=max(t2,d2[v][lg[dep[v]-dep[u]]]);
v=f[v][lg[dep[v]-dep[u]]];
}
for(int i=lg[dep[u]];i>=0;i--)
if(f[u][i]!=f[v][i])
{
if(t1<d1[v][i])
t2=t1,t1=d1[v][i];
else if(t1!=d1[v][i])
t2=max(t2,d1[v][i]);
else t2=max(t2,d2[v][i]);
v=f[v][i];
if(t1<d1[u][i])
t2=t1,t1=d1[u][i];
else if(t1!=d1[u][i])
t2=max(t2,d1[u][i]);
else t2=max(t2,d2[u][i]);
u=f[u][i];
}
if(u!=v)
{
if(t1<d1[v][0])
t2=t1,t1=d1[v][0];
else if(t1!=d1[v][0])
t2=max(t2,d1[v][0]);
else t2=max(t2,d2[v][0]);
if(t1<d1[u][0])
t2=t1,t1=d1[u][0];
else if(t1!=d1[u][0])
t2=max(t2,d1[u][0]);
else t2=max(t2,d2[u][0]);
}
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
e[i].u=read(),e[i].v=read(),e[i].w=read();
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;i++) ff[i]=i;
num=0;
for(int i=1;i<=m;i++)
{
int f1=find(e[i].u),f2=find(e[i].v);
if(f1!=f2)
{
s+=e[i].w;
ff[f1]=f2;
add(e[i].u,e[i].v,e[i].w);
add(e[i].v,e[i].u,e[i].w);
cnt++;
fl[i]=1;
}
if(num==n-1) break;
}
lg[0]=-1;
for(int i=1;i<=n;i++)
lg[i]=lg[i>>1]+1;
dfs(0,1);
ans=2e9;
for(int i=1;i<=m;i++)
if(!fl[i])
LCA(e[i].u,e[i].v),
ans=min(ans,e[i].w-((e[i].w==t1)?t2:t1));
printf("%lld\n",s+ans);
return 0;
}