题目链接
题意:给定n个点,m条边的图,要你输出对于一条边u->v来说,如果一定要让他加入生成树里,求有它的时候的生成树的最小值。
思路:我们先构造好初始的生成树,如果一条边u->v不在生成树里,那么那么我们要让新的生成树最小的话就得删去u->v的路径上的边权最大的边maxx就可以了,如果找u->v路径上的最大边呢?可以用树上带权倍增法。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+1;
int n,m,cnt=0,deep[maxn],parent[maxn][22],father[maxn],vis[maxn];
ll num[maxn][22],maxx,ans1[maxn],mm[maxn];
struct node{
int u,v,w,id;
}s[maxn],ss[maxn];
vector<pair<int,int>>g[maxn];
bool cmp(const node &a,const node &b)
{
return a.w<b.w;
}
int findfather(int x)
{
if(x==father[x]) return x;
int i=findfather(father[x]);
father[x]=i;
return i;
}
void dfs(int u,int fa)
{
deep[u]=deep[fa]+1;
parent[u][0]=fa;
for(auto to:g[u])
{
if(to.first==fa) continue;
num[to.first][0]=to.second;
dfs(to.first,u);
}
}
void init()
{
for(int k=1;k<=21;++k)
for(int i=1;i<=n;++i)
parent[i][k]=parent[parent[i][k-1]][k-1],num[i][k]=max(num[i][k-1],num[parent[i][k-1]][k-1]);
}
void lca(int u,int v)
{
if(deep[u]<deep[v]) swap(u,v);
if(u!=v)
for(int i=21;i>=0;--i) if(deep[parent[u][i]]>=deep[v]) maxx=max(maxx,num[u][i]),u=parent[u][i];
if(u==v) return ;
for(int i=21;i>=0;--i)
if(parent[u][i]!=parent[v][i]) maxx=max(maxx,max(num[u][i],num[v][i])),u=parent[u][i],v=parent[v][i];
maxx=max(maxx,max(num[u][0],num[v][0]));
}
ll kr()
{
for(int i=1;i<=m;++i) ss[i]=s[i];
sort(ss+1,ss+1+m,cmp);
ll ans=0;
for(int i=1;i<=m;++i)
{
int fa=findfather(ss[i].u),fb=findfather(ss[i].v),w=ss[i].w;
if(fa!=fb)
{
vis[ss[i].id]=1;
ans+=w;
cnt++;
father[fa]=fb;
g[ss[i].u].push_back({ss[i].v,ss[i].w});
g[ss[i].v].push_back({ss[i].u,ss[i].w});
if(cnt==n-1) break;
}
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n;++i) father[i]=i;
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&s[i].u,&s[i].v,&s[i].w);
s[i].id=i;
}
ll ans=kr();
deep[1]=1;
dfs(1,0);
init();
for(int i=1;i<=m;++i)
{
if(vis[i]) printf("%lld\n",ans);
else {
maxx=0;
lca(s[i].u,s[i].v);
printf("%lld\n",ans-maxx+s[i].w);
}
}
}