【XSY2515】管道(pipe)(LCA+最小生成树)
原题链接
\(Description\)
给你一个城市下水道网络图,你需要选出一些管道,使得在只使用这些管道的情况下,令整个网络联通,并且花费最小。
网络图可以看做是无向连通图,有\(n\)个节点和\(m\)条边,每条边连接\(u_{i}\)和\(v_{i}\),选择的花费是\(w_{i}\)。
不巧的是,由于某些原因,现在市政局要求选定某条特定的边管道,你的任务是求出对于某一条边,在选择这条管道的前提下的最小花费。
\(Input\)
第\(1\)行包含两个整数\(n\),\(m\),表示点数和边数。
第\(2\sim m+1\)行每行三个整数\(u_{i}\),\(v_{i}\),\(w_{i}\),表示有一条管道连接\(u_{i}\)和\(v_{i}\),费用为\(w_{i}\)。
\(Output\)
输出\(m\)行,每行一个整数,表示选择第\(i\)条管道的前提下的最小花费。
管道按输入的顺序编号为\(1\sim 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\)
保证初始图连通
思路
这道题其实十分简单
我们先求出一棵最小生成树
然后,我们取第\(i\)条边,易得这条边所在的最小生成树中的其他边都是原最小生成树中的边
于是,我们可以将\(u_{i}\)到\(v_{i}\)上的一些边去掉,为了满足最小,当然要选择最大的边删除
对于维护最大边,我们考虑用\(LCA\)倍增维护
\(ps:\)不开\(long\) \(long\)见祖宗,数组开小见阎王
#include<bits/stdc++.h>
using namespace std;
const long long N=200010;
long long n,m,tot=0;
long long fa[N];
long long f[N][30];
long long maxn[N][30];
long long dep[N];
long long head[N];
long long ans[N];
bool vis[N];
struct road
{
long long to,nxt,dis,id;
}e[N<<1];
struct edge
{
long long u,v,w,id;
}g[N];
long long find(long long a)
{
if(a==fa[a])return a;
return fa[a]=find(fa[a]);
}
void dfs(long long u)
{
for(long long i=1;i<=18;i++)
{
f[u][i]=f[f[u][i-1]][i-1];//更新倍增信息
maxn[u][i]=max(maxn[u][i-1],maxn[f[u][i-1]][i-1]);
}
for(long long i=head[u];i;i=e[i].nxt)
{
long long v=e[i].to,dis=e[i].dis,idx=e[i].id;
if(v!=f[u][0])
{
f[v][0]=u;
maxn[v][0]=dis;//维护最大边
dep[v]=dep[u]+1;
dfs(v);
}
}
}
long long get(long long x,long long y)
{
long long p=0;
if(dep[x]<dep[y])swap(x,y);
for(long long i=18;i>=0;i--)
{
if(dep[f[x][i]]>=dep[y])
{
p=max(p,maxn[x][i]);//维护最大边
x=f[x][i];
}
}
if(x==y)return p;
for(long long i=18;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
p=max(p,max(maxn[x][i],maxn[y][i]));//最大边
x=f[x][i],y=f[y][i];
}
}
return max(p,max(maxn[x][0],maxn[y][0]));
}
void add(long long u,long long v,long long w,long long idx)
{
e[++tot]=(road){v,head[u],w,idx};head[u]=tot;
e[++tot]=(road){u,head[v],w,idx};head[v]=tot;
}
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
inline long long read()
{
long long x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*f;
}
int main()
{
n=read(),m=read();
for(long long i=1;i<=m;i++)g[i].u=read(),g[i].v=read(),g[i].w=read(),g[i].id=i;
sort(g+1,g+m+1,cmp);
for(long long i=1;i<=n;i++)fa[i]=i;
long long sum=0;
for(long long i=1;i<=m;i++)
{
long long fx=find(g[i].u);
long long fy=find(g[i].v);
if(fx!=fy)
{
fa[fy]=fx;
sum+=g[i].w;
add(g[i].u,g[i].v,g[i].w,g[i].id);
vis[i]=1;
}
}
//最小生成树
dep[1]=1;
dfs(1);
for(long long i=1;i<=n;i++)fa[i]=i;
for(long long i=1;i<=m;i++)
{
long long x=g[i].u,y=g[i].v,z=g[i].w,idx=g[i].id;
if(vis[i])//如果在原最小生成树中
{
ans[idx]=sum;
continue;
}
long long summ=get(x,y);
ans[idx]=sum+g[i].w-summ;//原最小生成树加上这条边权值再减去边上最大值
}
for(long long i=1;i<=m;i++)printf("%lld\n",ans[i]);
return 0;
}
/*
5 7
1 2 3
1 3 1
1 4 5
2 3 2
2 5 3
3 4 2
4 5 4
*/
梅子满树,清酒伊人