[Contest1336]管道(pipe)
题面
Description
给你一个城市下水道网络图,你需要选出一些管道,使得在只使用这些管道的情况下,令整个网络联通,并且花费最小。
网络图可以看做是无向连通图,有n个节点和m条边,每条边连接ui和vi,选择的花费是wi。
不巧的是,由于某些原因,现在市政局要求选定某条特定的边管道,你的任务是求出对于某一条边,在选择这条管道的前提下的最小花费。
Input
第1行包含两个整数n,m,表示点数和边数。
第2~m+1行每行三个整数ui,vi,wi,表示有一条管道连接ui和vi,费用为wi。
Output
输出m行,每行一个整数,表示选择第i条管道的前提下的最小花费。
管道按输入的顺序编号为1~m。
Sample Input
5 7
1 2 3
1 3 1
1 4 5
2 3 2
2 5 3
3 4 2
4 5 4
Sample Output
9
8
11
8
8
8
9
Hint
对于20%的数据,n<=1000,m<=2000
对于另外20%的数据,m<=n+10
对于100%的数据,2<=n<=100000,1<=m<=200000
保证初始图连通
题意
每次选1条边必须出现,求最小生成树。
题解
简单模拟后发现可以先求出最小生成树,记录最小生成树中的边,每选择1条边时,
如果存在于最小生成树中,答案就是最小生成树;
否则,这条边连接的两个点必定和它们的LCA构成一个环,所以我们应该选择其中最大的一条边删除,可以通过倍增预处理,在lca的时候取最值。
#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+5;
long long n,m,cnt,head[N],fa[N],dp[N],f[N][20],mx[N][20];
long long sum;
bool vis[N];
struct edge1{
long long u,v,w,id;
}e1[N<<2];
struct edge2{
long long v,w,next;
}e2[N<<2];
void add(int u,int v,long long w){
e2[++cnt]=(edge2){v,w,head[u]};
head[u]=cnt;
}
bool cmp(edge1 a,edge1 b){
return a.w<b.w;
}
void dfs(int u){
for(int i=head[u];i;i=e2[i].next){
int v=e2[i].v;
if(v==f[u][0])continue;
f[v][0]=u;
mx[v][0]=e2[i].w;
dp[v]=dp[u]+1;
dfs(v);
}
}
long long lca(int x,int y){
long long ans=0;
if(dp[x]<dp[y])swap(x,y);
for(int i=17;i>=0;i--){
if(dp[f[x][i]]>=dp[y]){
ans=max(ans,1ll*mx[x][i]);
x=f[x][i];
}
}
if(x==y)return ans;
for(int i=17;i>=0;i--){
if(f[x][i]!=f[y][i]){
ans=max(ans,1ll*max(1ll*mx[x][i],1ll*mx[y][i]));
x=f[x][i];y=f[y][i];
}
}
ans=max(ans,1ll*max(1ll*mx[x][0],1ll*mx[y][0]));
return ans;
}
int get(int x){
return x==fa[x]?x:fa[x]=get(fa[x]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){//建两层边,为了处理答案方便
int u,v,w;
scanf("%d%d%lld",&e1[i].u,&e1[i].v,&e1[i].w);
e1[i].id=i;
e1[i+m]=e1[i];
}
sort(e1+1+m,e1+1+m+m,cmp);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=m+1;i<=m*2;i++){//最小生成树
int u=get(e1[i].u),v=get(e1[i].v);
if(u!=v){
fa[u]=v;
sum+=(long long)e1[i].w;
vis[e1[i].id]=1;
add(e1[i].u,e1[i].v,e1[i].w);//建树
add(e1[i].v,e1[i].u,e1[i].w);
}
}
dfs(1);//深搜,lca预处理
for(int i=1;i<=17;i++){
for(int j=1;j<=n;j++){
f[j][i]=f[f[j][i-1]][i-1];
mx[j][i]=max(1ll*mx[j][i-1],1ll*mx[f[j][i-1]][i-1]);
}
}
for(int i=1;i<=m;i++){//两种情况
if(vis[i])printf("%lld\n",sum);
else printf("%lld\n",sum-(long long)lca(e1[i].u,e1[i].v)+e1[i].w);
}
}

浙公网安备 33010602011771号