[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); } }