题目链接:https://codeforces.com/problemset/problem/609/E
题意:n个点m条边,对于每条边,求包含这条边的MST的权重。
思路:首先求出一个MST,假设其权重为sum。对于每条边,如果其在MST上,那么答案就是sum;否则,我们在求得的MST加上这条边(假设权重为w1),那么树出现了环。我们要在树上u->v的路径中删除权值最大的一条边(假设为w2),那么答案就是sum-w1+w2。求次小生成树也是这种思想。
实现1:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+10;
//Kruscal求最小生成树部分
struct node{
int id,from,to,w;
}g[N];
vector<node> tg[N];
int n,m,f[N],limit;
ll ans[N],sum=0;
bool book[N];
int getf(int x){ return x==f[x]?x:f[x]=getf(f[x]); }
void add(int u,int v,int w){
tg[u].push_back(node{0,u,v,w});
tg[v].push_back(node{0,v,u,w});
}
bool cmp(node a,node b){
return a.w<b.w;
}
//树上倍增求LCA部分
int dep[N],dp[N][30],fa[N][30];
//dp[i][j]:代表从i向上走(1<<j)步的路径中最大的边权。
//fa[i][j]: 代表从i向上走(1<<j)步所到的点。
void dfs(int u,int fath,int deep,int w){
fa[u][0]=fath;
dp[u][0]=w;
dep[u]=deep;
int siz=tg[u].size();
for(int i=0;i<siz;i++){
int v=tg[u][i].to;
if(v==fath) continue;
dfs(v,u,deep+1,tg[u][i].w);
}
return ;
}
int lca(int x,int y){
int maxx=0;
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[fa[x][i]]>=dep[y]){
maxx=max(maxx,dp[x][i]),x=fa[x][i];
}
if(x==y) return maxx;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
maxx=max(maxx,dp[x][i]),maxx=max(maxx,dp[y][i]),x=fa[x][i],y=fa[y][i];
maxx=max(maxx,dp[x][0]),maxx=max(maxx,dp[y][0]);
return maxx;
}
int main(void){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[i]=node{i,u,v,w};
}
sort(g+1,g+1+m,cmp);
for(int i=1;i<=m;i++){
int fu=getf(g[i].from),fv=getf(g[i].to);
if(f[fu]!=f[fv]){
f[fu]=fv;
add(g[i].from,g[i].to,g[i].w);
sum+=g[i].w;
book[i]=1;
}
}
dfs(1,-1,1,0);
for(int i=1;(1<<i)<=n;i++)
for(int j=1;j<=n;j++){
dp[j][i]=dp[j][i-1];//这一步至关重要 ,赋初值。
fa[j][i]=fa[fa[j][i-1]][i-1];
dp[j][i]=max(dp[j][i],dp[fa[j][i-1]][i-1]);
}
for(int i=1;i<=m;i++)
if(book[i]) ans[g[i].id]=sum;
else ans[g[i].id]=sum-lca(g[i].from,g[i].to)+g[i].w;
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}
实现2:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+10;
//Kruscal求最小生成树部分
struct node{
int id,from,to,w;
}g[N];
vector<node> tg[N];
int n,m,f[N],limit;
ll ans[N],sum=0;
bool book[N];
int getf(int x){ return x==f[x]?x:f[x]=getf(f[x]); }
void add(int u,int v,int w){
tg[u].push_back(node{0,u,v,w});
tg[v].push_back(node{0,v,u,w});
}
bool cmp(node a,node b){
return a.w<b.w;
}
//树上倍增求LCA部分
int dep[N],dp[N][30],fa[N][30];
//dp[i][j]:代表从i向上走(1<<j)步的路径中最大的边权。
//fa[i][j]: 代表从i向上走(1<<j)步所到的点。
void dfs(int u,int fath,int deep,int w){
fa[u][0]=fath;
dp[u][0]=w;
dep[u]=deep;
for(int j=1;(1<<j)<=n;j++){
dp[u][j]=dp[u][j-1];//这一步至关重要 ,赋初值。
fa[u][j]=fa[fa[u][j-1]][j-1];
dp[u][j]=max(dp[u][j],dp[fa[u][j-1]][j-1]);
}
int siz=tg[u].size();
for(int i=0;i<siz;i++){
int v=tg[u][i].to;
if(v==fath) continue;
dfs(v,u,deep+1,tg[u][i].w);
}
return ;
}
int lca(int x,int y){
int maxx=0;
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[fa[x][i]]>=dep[y]){
maxx=max(maxx,dp[x][i]),x=fa[x][i];
}
if(x==y) return maxx;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
maxx=max(maxx,dp[x][i]),maxx=max(maxx,dp[y][i]),x=fa[x][i],y=fa[y][i];
maxx=max(maxx,dp[x][0]),maxx=max(maxx,dp[y][0]);
return maxx;
}
int main(void){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[i]=node{i,u,v,w};
}
sort(g+1,g+1+m,cmp);
for(int i=1;i<=m;i++){
int fu=getf(g[i].from),fv=getf(g[i].to);
if(f[fu]!=f[fv]){
f[fu]=fv;
add(g[i].from,g[i].to,g[i].w);
sum+=g[i].w;
book[i]=1;
}
}
dfs(1,0,1,0);
for(int i=1;i<=m;i++)
if(book[i]) ans[g[i].id]=sum;
else ans[g[i].id]=sum-lca(g[i].from,g[i].to)+g[i].w;
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}