【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
*/
posted @ 2019-08-22 21:35  ShuraEye  阅读(171)  评论(0编辑  收藏  举报