Description
给出n个点,m条有权边,现对于每一条边,你需要回答出包含这条边的最小生成树的总边权值。
原题在:CF609E
Solution
对原图做一遍最小生成树,那么在最小生成树上的边答案就是该最小生成树边权和。
原图没有重边,这说明任选 n−1 条不构成环的边都可以构成生成树。
对于不在最小生成树上的边 (u,v) ,我们知道树中最短路径是唯一的,如果添加了 (u,v) ,那么就会生成环,这时我们只要删掉原最小生成树上的 u−>v 路径上最大的边,就可以再次将原图变为包含 (u,v) 的最小生成树。
运用树上倍增算法可以达到 O(nlog2n)
Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 200001
#define M 400001
#define ll long long
using namespace std;
struct node{
int u,v,w,t;
}b[N];
int f[N];
int wz[N];
ll c[N];
int to[M],next[M],last[M],val[M],num=0;
int fa[N][21],mx[N][21];
int d[N];
void link(int x,int y,int c)
{
num++;
to[num]=y;
next[num]=last[x];
last[x]=num;
val[num]=c;
}
bool cmp(node x,node y)
{
return x.w<y.w;
}
int find(int x)
{
return !f[x]?x:f[x]=find(f[x]);
}
void dfs(int x)
{
for(int i=last[x];i;i=next[i])
{
int v=to[i];
if(v!=fa[x][0])
{
fa[v][0]=x;
mx[v][0]=val[i];
d[v]=d[x]+1;
dfs(v);
}
}
}
int work(int x,int y)
{
if(d[x]<d[y]) swap(x,y);
int tmp=0;
fd(i,20,0)
if(fa[x][i] && d[fa[x][i]]>=d[y])
tmp=max(tmp,mx[x][i]),x=fa[x][i];
fd(i,20,0)
if(fa[x][i]!=fa[y][i] && fa[x][i] && fa[y][i])
{
tmp=max(tmp,mx[x][i]);
x=fa[x][i];
tmp=max(tmp,mx[y][i]);
y=fa[y][i];
}
if(x!=y)
{
tmp=max(tmp,mx[x][0]);
tmp=max(tmp,mx[y][0]);
}
return tmp;
}
int main()
{
freopen("street.in","r",stdin);
freopen("street.out","w",stdout);
int n,m;
cin>>n>>m;
fo(i,1,m)
{
scanf("%d %d %d",&b[i].u,&b[i].v,&b[i].w);
b[i].t=i;
}
sort(b+1,b+m+1,cmp);
fo(i,1,m) wz[b[i].t]=i;
int z=0;
ll tt=0;
fo(i,1,m)
{
int fx=find(b[i].u),fy=find(b[i].v);
if(fx!=fy)
{
link(b[i].u,b[i].v,b[i].w);
link(b[i].v,b[i].u,b[i].w);
f[fy]=fx;
tt+=b[i].w;
c[b[i].t]=-1;
z++;
if(z==n-1) break;
}
}
fo(i,1,m)
if(c[i]<0) c[i]=tt;
d[0]=-1;
dfs(1);
fo(j,1,20)
fo(i,1,n)
{
fa[i][j]=fa[fa[i][j-1]][j-1];
mx[i][j]=max(mx[i][j-1],mx[fa[i][j-1]][j-1]);
}
fo(q,1,m)
if(!c[q])
{
int u=b[wz[q]].u,v=b[wz[q]].v,w=b[wz[q]].w;
int zz=work(u,v);
c[q]=tt-zz+w;
}
fo(i,1,m) printf("%lld\n",c[i]);
}