【BJOI2010】次小生成树-最小生成树+倍增LCA
题目大意:给定一个带边权的无向图,求该图中严格次小生成树上的边权和。
做法:本题需要用到最小生成树+倍增LCA。
所谓严格次小生成树,就是边权和严格大于最小生成树的边权和最小的生成树。我们求非严格次小生成树时,是对于每一条不在最小生成树中的边,将它加入后,再从构成的环中去掉一个除了它之外最大的边,所有这样构成的树中边权和最小的就是次小生成树了。然而这次要求严格次小生成树,这就意味着它的边权和必须严格大于最小生成树,所以对于每条不在最小生成树中的边,如果它和它两个端点在最小生成树上路径中最大边相等,就要用严格次大的边来作为被删掉的边。树上两点间严格次大的边权可以用倍增LCA来找,具体的预处理方法这里解释不清,请看代码吧。这样一来,时间复杂度是
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
int n,m;
ll totalsum,ans=2000000000;
int first[100010],tot=0,f[100010];
int fa[100010][21]={0},mx[100010][21]={0},smx[100010][21]={0},dep[100010]={0};
struct edge {int v,next;ll d;} e[200010];
struct forsort {int x,y;ll z;} a[300010];
bool chosen[300010]={0};
bool cmp(forsort a,forsort b)
{
return a.z<b.z;
}
int find(int x)
{
int r=x,i=x,j;
while(f[r]!=r) r=f[r];
while(i!=r)
{
j=f[i];
f[i]=r;
i=j;
}
return r;
}
void merge(int a,int b)
{
int fa=find(a),fb=find(b);
f[fa]=fb;
}
void insert(forsort a)
{
e[++tot].v=a.y,e[tot].d=a.z,e[tot].next=first[a.x],first[a.x]=tot;
e[++tot].v=a.x,e[tot].d=a.z,e[tot].next=first[a.y],first[a.y]=tot;
}
void kruskal()
{
sort(a+1,a+m+1,cmp);
int now=0;totalsum=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
{
if (now==n-1) break;
int fx=find(a[i].x),fy=find(a[i].y);
if (fx!=fy)
{
now++;
totalsum+=a[i].z;
chosen[i]=1;
insert(a[i]);
merge(a[i].x,a[i].y);
}
}
}
void dfs(int v)
{
smx[v][0]=-1;
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa[v][0])
{
fa[e[i].v][0]=v;
mx[e[i].v][0]=e[i].d;
dep[e[i].v]=dep[v]+1;
dfs(e[i].v);
}
}
void calc(ll &val1,ll val2,ll &val3,ll val4)
{
ll v1,v2;
v1=max(val1,val2);
if (val3>=val2) v2=val3;
else if (val4>=val1) v2=val4;
else if (val1!=val2) v2=min(val1,val2);
else v2=max(val3,val4);
val1=v1,val3=v2;
}
ll query(ll d,int x,int y)
{
ll mxx=0,smxx=0;
if (dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if (dep[fa[x][i]]>=dep[y])
{
calc(mxx,mx[x][i],smxx,smx[x][i]);
x=fa[x][i];
}
if (x!=y)
{
for(int i=20;i>=0;i--)
if (fa[x][i]!=fa[y][i])
{
calc(mxx,mx[x][i],smxx,smx[x][i]);
calc(mxx,mx[y][i],smxx,smx[y][i]);
x=fa[x][i],y=fa[y][i];
}
calc(mxx,mx[x][0],smxx,smx[x][0]);
calc(mxx,mx[y][0],smxx,smx[y][0]);
}
if (d==mxx&&smxx!=-1) return smxx;
else return mxx;
}
void work()
{
dfs(1);
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
{
fa[j][i]=fa[fa[j][i-1]][i-1];
ll val1=mx[j][i-1],val2=mx[fa[j][i-1]][i-1],val3=smx[j][i-1],val4=smx[fa[j][i-1]][i-1];
mx[j][i]=max(val1,val2);
if (val3>=val2) smx[j][i]=val3;
else if (val4>=val1) smx[j][i]=val4;
else if (val1!=val2) smx[j][i]=min(val1,val2);
else smx[j][i]=max(val3,val4);
}
for(int i=1;i<=m;i++)
if (!chosen[i])
{
ll val=query(a[i].z,a[i].x,a[i].y);
if (val!=-1) ans=min(ans,a[i].z-val);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].z);
kruskal();
work();
printf("%lld",totalsum+ans);
return 0;
}