我终于搞懂了!!!
题目大意
求严格次小生成树。
数据中无向图无自环;
50%的数据N≤2000 M≤3000;
80%的数据N≤50000 M≤100000;
100%的数据N≤100000 M≤300000,边权值非负且不超过10^9。
题目分析
墙裂推荐这个blog!!!
思路看懂了吗???
然后直接讲代码???
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
bool v[300010];
int n,m,len=0,c[30],deep[100010];
struct nod1{int x,y,z;}d[300010];
struct nod2{int y,gg,c;}b[200010];
int max1[100010][30],max2[100010][30];
int fa[100010],a[100010],f[100010][30];
int cmp(nod1 x,nod1 y) { return x.z<y.z; }
int find(int x)//并并查集
{
if(fa[x]!=x) return fa[x]=find(fa[x]);
else return fa[x];
}
void ins(int x,int y,int c)
{
len++; b[len].y=y; b[len].c=c;
b[len].gg=a[x]; a[x]=len;
}
void dfs(int x,int fa)
{
f[x][0]=fa; deep[x]=deep[fa]+1;
for(int i=1;i<=21;i++)
{
if(c[i]>deep[x]) break;
//我往上跳的高度不能高于我的深度
f[x][i]=f[f[x][i-1]][i-1];
//倍增求LCA,不懂就画图画图
}
for(int i=a[x];i;i=b[i].gg)
{
int y=b[i].y;
if(y!=fa)
{
max1[y][0]=b[i].c;//y到x的距离
dfs(y,x);
}
}
}
int lca(int x,int y)//找最近公共祖先
{
if(deep[x]>deep[y]) swap(x,y);
for(int i=21;i>=0;i--)//先跳到同一深度
{
if(deep[x]==deep[y]) break;
if(deep[x]<=deep[y]-c[i]) y=f[y][i];
//y跳到的深度必须大于等于x的深度
}
if(x==y) return x;
for(int i=21;i>=0;i--)
{
if(deep[x]<=c[i]||f[x][i]==f[y][i]) continue;
//往上跳的高度不能高于我的深度并且x,y跳到的位置不能相同
//这个不懂要先去学习 倍增求LCA 啊
x=f[x][i]; y=f[y][i];
}
return f[x][0];
}
int getmax(int x,int y,int ma)
{
int kk=0;
for(int i=21;i>=0;i--)
{
if(x==y) break;
if(deep[f[x][i]]>=deep[y])
{
if(max1[x][i]!=ma) kk=max(kk,max1[x][i]);
//如果max1==ma说明删去的边和加上的边权值相同,显然不符合题意
else kk=max(kk,max2[x][i]);//那么ma等于次长
x=f[x][i];
}
}
return kk;
}
int main()
{
memset(v,false,sizeof(v));
scanf("%d%d",&n,&m); c[0]=1;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=21;i++) c[i]=c[i-1]*2;//2的i次方
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&d[i].x,&d[i].y,&d[i].z);
}
sort(d+1,d+1+m,cmp);
int t=0; long long sum=0;
for(int i=1;i<=m;i++)
{
int fx=find(d[i].x),fy=find(d[i].y);
if(fx!=fy)
{
t++; sum+=d[i].z;
fa[fx]=fy; v[i]=true;
ins(d[i].x,d[i].y,d[i].z);
ins(d[i].y,d[i].x,d[i].z);
}
if(t==n-1) break;
}
//最小生成树
//printf("sum==%d\n",sum);
deep[0]=0; dfs(1,0);
//找最长边(max1)和次长边(max2)
for(int i=1;i<=21;i++)
{
for(int x=1;x<=n;x++)
{
//找最长边和次长边
max1[x][i]=max(max1[x][i-1],max1[f[x][i-1]][i-1]);
max2[x][i]=max(max2[x][i-1],max2[f[x][i-1]][i-1]);
//如果你没有搞清楚他怎么跳,为什么就得出上面的式子
//建议你画个图手动模拟一下
int aa=max1[x][i-1],bb=max1[f[x][i-1]][i-1];
if(aa>bb) max2[x][i]=max(max2[x][i],bb);
else if(aa<bb) max2[x][i]=max(max2[x][i],aa);
}
}
long long ans=1e18;
for(int i=1;i<=m;i++)
{
if(!v[i])
{
int lcaa=lca(d[i].x,d[i].y);
int aa=getmax(d[i].x,lcaa,d[i].z);
int bb=getmax(d[i].y,lcaa,d[i].z);
//ma==x到y途中不等于删去边的最长边
//ans=最小生成树和-ma+非树边边权
ans=min(ans,sum+d[i].z-max(aa,bb));
}
}
printf("%lld",ans);
return 0;
}