码迷,mamicode.com
首页 > 其他好文 > 详细

【XSY2515】管道(pipe)(最小生成树+倍增lca)

时间:2019-09-14 23:04:43      阅读:237      评论:0      收藏:0      [点我收藏+]

标签:描述   维护   联通   getc   while   就是   text   bit   ado   

题面

Description

给你一个城市下水道网络图,你需要选出一些管道,使得在只使用这些管道的情况下,令整个网络联通,并且花费最小。

网络图可以看做是无向连通图,有\(n\)个节点和\(m\)条边,每条边连接\(u_i\)\(v_i\),选择的花费是\(w_i\)

不巧的是,由于某些原因,现在市政局要求选定某条特定的边管道,你的任务是求出对于某一条边,在选择这条管道的前提下的最小花费。

Input

\(1\)行包含两个整数\(n\)\(m\),表示点数和边数。

\(2\)~\(m+1\)行每行三个整数\(u_i\)\(v_i\)\(w_i\),表示有一条管道连接\(u_i\)\(v_i\),费用为\(w_i\)

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<=u_i,v_i<=n<=100000\)\(1<=m<=200000\)\(w_i<=2^{31}\)

保证初始图连通。

题解

题目就是求包含某条边的最小生成树。

先把原图的最小生成树求出来。

枚举图上的每一条边,考虑选择这条管道的前提下的最小花费:

  1. 如果这条边就在最小生成树上,显然,最小花费就是最小生成树的边权和。

  2. 如果这条边不在最小生成树上,如下图中的边\((5,6)\)

    技术图片

    显然如果我们加入了边\((5,6)\),就会构成一个环,这个环的一部分就是\((5,6)\),另一部分是在树上的\(5\)\(6\)的路径,即\(5\longrightarrow3\longrightarrow4\longrightarrow6\)

    这样如果我们去掉环上的任意一条边,所有点还是联通的,且边权和比没去时更小。

    又因为我们不能去掉边\((5,6)\),所以我们只能去掉原图的最小生成树上的\(5\)\(6\)的路径的任意一边。

    且为了保证去掉这条边后边权和最小,我们要去掉这条路径上边权最大的边。

这样两种情况都讨论完了,至于第二种情况的路径上边权最大值怎么维护,可以用树剖或者倍增。

代码如下:

#include<bits/stdc++.h>

#define N 1000010
#define M 2000010
#define int long long
#define ll long long

using namespace std;

struct edge
{
    int u,v,w,id;
}e[M];

int n,m,fa[N];
int f[N][20],maxn[N][20],d[N];
int cnt,head[N],nxt[N<<1],to[N<<1],w[N<<1];
ll ans,Ans[N];
bool flag[N];

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<3)+(x<<1)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}

int find(int x)
{
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);
}

inline bool cmp(edge a,edge b)
{
    return a.w<b.w;
}

inline void adde(int u,int v,int wi)
{
    to[++cnt]=v;
    w[cnt]=wi;
    nxt[cnt]=head[u];
    head[u]=cnt;
}

void dfs(int u)
{
    for(int 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(int i=head[u];i;i=nxt[i])
    {
        if(to[i]!=f[u][0])
        {
            f[to[i]][0]=u;
            maxn[to[i]][0]=w[i];
            d[to[i]]=d[u]+1;
            dfs(to[i]);
        }
    }
}

inline int LCA(int a,int b)//a->lca->b
{
    int maxx=0;
    if(d[a]<d[b])
        swap(a,b);
    for(int i=18;i>=0;i--)
        if(d[f[a][i]]>=d[b])    
            maxx=max(maxx,maxn[a][i]),a=f[a][i];
    if(a==b)
        return maxx;
    for(int i=18;i>=0;i--)
    {
        if(f[a][i]!=f[b][i])
        {
            maxx=max(maxx,max(maxn[a][i],maxn[b][i]));
            a=f[a][i],b=f[b][i];
        }
    }
    maxx=max(maxx,max(maxn[a][0],maxn[b][0]));
    return maxx;
}

signed main()
{
    n=read(),m=read();
    for(register int i=1;i<=n;i++)
        fa[i]=i;
    for(register int i=1;i<=m;i++)
    {
        e[i].u=read(),e[i].v=read(),e[i].w=read();
        e[i].id=i;
    }
    sort(e+1,e+m+1,cmp);
    for(register int i=1,num=0;i<=m;i++)//最小生成树
    {
        if(num==n-1)
            break;
        int a=find(e[i].u),b=find(e[i].v);
        if(a!=b)
        {
            fa[a]=b;
            adde(e[i].u,e[i].v,e[i].w);
            adde(e[i].v,e[i].u,e[i].w);
            ans+=e[i].w;
            flag[i]=true;
            num++;
        }
    }
    d[1]=1;
    dfs(1);
    for(register int i=1;i<=m;i++)
    {
        if(flag[i])//这条边在最小生成树
        {
            Ans[e[i].id]=ans;
            continue;
        }
        int maxx=LCA(e[i].u,e[i].v);//不在就求u、v路径上的边权最大值
        Ans[e[i].id]=ans-maxx+e[i].w;
    }
    for(register int i=1;i<=m;i++)
        printf("%lld\n",Ans[i]);
    return 0;
}

另,相似题:XSY2485,以及题解

【XSY2515】管道(pipe)(最小生成树+倍增lca)

标签:描述   维护   联通   getc   while   就是   text   bit   ado   

原文地址:https://www.cnblogs.com/ez-lcw/p/11520258.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!