zoukankan      html  css  js  c++  java
  • 2019暑假集训DAY4(problem3.construct)(网络流)

    改编自BZOJ2521 [Shoi2010]最小生成树

    题面

    1    construct

    1.1       Description

    Me懒得编题面了

    给出一个n个点m条边的图,每条边有边长。现在我们钦定了一条边,要使该边一定出现在最小生成树中

    你可以进行如下操作:将某条边的长度+1,并付出1的代价

    最少需要付出多少代价可以满足要求呢?

    1.2       Input

    第一行三个整数 ,分别表示点数,边数,被钦定的边的编号

    接下来 行,每行三个整数 ,表示一条连接 的路径,其长度为

     

    1.3       Output

    输出一行一个数字,表示最少操作次数

     

    1.4       Sample Input

    4 6 1

    1 2 2

    1 3 2

    1 4 3

    2 3 2

    2 4 4

    3 4 5

     1.5      Sample output

    1

    解释:将1放在中间,2 , 3 , 4号点放在1的周围可以画出此图。容易发现,对连接2,3的边进行一次操作即可满足要求

     

    1.6       Note

    对于全部数据,满足 ,数据有梯度

    保证有 20% 的数据(均匀分布),满足1<=n<=500,1<=M<=800,1<=d<=1e6

    保证有 20% 的数据(均匀分布),满足编号为 的边是一个桥(由于一些特殊原因,保证保证不存在)


    其实这道题暴力可以过(没想到吧?可能是wys大佬的数据原因)

    我们可以先求出最小生成树(设钦定边的边权为L)

    如果钦定的边在树中则不需要付出代价(注意:分析样例发现,可能存在和钦定边相同大小的非树边可以代替它,所以在排序的时候可以做一点小处理,在边权相等的边中,把钦定边往后丢

    如果钦定的边不在树中,则可以借鉴求次小生成树的做法,把钦定边丢进树中,则会形成一个环,在这个环中,试着是每一条边(除了钦定边)增大好使得使用钦定边,删掉原树边。但这时我们会发现一个问题,可能增大了某条树边后(边权变为L+1),存在在钦定边的前面还有一条更小的可以加入树中。对此,我们可以维护一个并查集,对于要删去的树边,删去后会分成两个连通块,我们判断在钦定边前面都没有这样的一条边,使得两个块可以连接起来(自己画图吧!这里不想再画了rua)。具体怎么操作呢?暴力就可以了,每次都把并查集清空,又加入两个块中的所有的边,判断时看father是不是同一个就行了,如果存在这样的边就把这条边的边权加到L+1,然后重复操作(我们可以看到n的范围是500)。

    这里贴出mwy的代码(因为我没打)

    #include<bits/stdc++.h>
    using namespace std;
    #define N 505
    #define M 805
    int n,m,k,ans=0x7f7f7f,fa1[N],fa2[N],head[N],nex[N<<1],w[N<<1],to[N<<1],tot=0,f[N];
    int dep[N],val;
    struct node{ int x,y,w,o,val; }ap[M];
    bool cmp(const node a,const node b){
        if(a.w==b.w) return a.val>b.val;
        return a.w<b.w; 
    }
    void add(int a,int b,int ww) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=ww; }
    int get1(int x)
    {
        if(x==fa1[x]) return x;
        return fa1[x]=get1(fa1[x]);
    }
    int get2(int x)
    {
        if(x==fa2[x]) return x;
        return fa2[x]=get2(fa2[x]);
    }
    void dfs1(int u,int father)
    {
        
        for(int i=head[u];i;i=nex[i]){
            int v=to[i];
            if(v==father) continue;
            f[v]=u; dep[v]=dep[u]+1;
            dfs1(v,u);
        }
    }
    void dfs2(int u,int father,int fl,int x)
    {
        for(int i=head[u];i;i=nex[i]){
            int v=to[i];
            if(v==father) continue;
            if(fl&&v==x)  continue;
            int f1=get2(u),f2=get2(v);
            if(f1!=f2) fa2[f1]=f2;
            dfs2(v,u,fl,x);
        }
    }
    int work(int a,int b)
    {
        int sum=0;
        for(int i=1;i<=n;i++) fa2[i]=i;
        dfs2(a,f[a],0,0),dfs2(b,0,1,a);//
        for(int i=1;i<=m;i++){
            if(ap[i].o==k) break;
            int f1=get2(ap[i].x),f2=get2(ap[i].y);
            if(f1!=f2) sum+=(val+1-ap[i].w);
        }
        return sum;
    }
    void lca(int a,int b)
    {
        if(dep[a]<dep[b]) swap(a,b);
        while(dep[f[a]]>=dep[b]){
            ans=min(ans,work(a,b)); a=f[a];
        }
        if(a==b) { printf("%d
    ",ans); return ; }
        while(f[a]!=f[b]){
            ans=min(ans,work(a,b)); a=f[a];//direction
            ans=min(ans,work(b,a)); b=f[b];
        }/**/
        
        ans=min(ans,work(a,b));//
        ans=min(ans,work(b,a));
        printf("%d
    ",ans);
    }
    int main()
    {
        freopen("construct.in","r",stdin);
        freopen("construct.out","w",stdout);
        int x,y,fll=0;
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=m;i++)
        scanf("%d%d%d",&ap[i].x,&ap[i].y,&ap[i].w),ap[i].o=i;
        ap[k].val=-1; x=ap[k].x,y=ap[k].y; val=ap[k].w;
        sort(ap+1,ap+1+m,cmp);
        int cnt=0;
        for(int i=1;i<=n;i++) fa1[i]=i;
        for(int i=1;i<=m;i++){
            int f1=get1(ap[i].x); int f2=get1(ap[i].y);
            if(f1!=f2){
                if(ap[i].o==k){ fll=1; break; }
                fa1[f1]=f2;
                add(ap[i].x,ap[i].y,ap[i].w),add(ap[i].y,ap[i].x,ap[i].w);
                cnt++;
            }
            if(cnt==n-1) break;
        }
        if(fll) { printf("0
    "); return 0; }
        f[1]=0; dep[1]=1;
        dfs1(1,0);
        lca(x,y);
        return 0;
    }
    /*
    8 18 2
    6 1 46
    3 2 63
    7 3 33
    5 6 66
    4 7 65
    5 4 61
    8 5 33
    7 8 33
    4 2 27
    1 7 23
    5 8 10
    6 8 41
    2 3 91
    1 4 72
    7 1 89
    4 8 16
    3 5 11
    2 8 77
    
    9 10 2
    3 6 88
    7 8 100
    5 6 87
    6 8 1
    1 3 2
    2 5 3
    5 9 4
    2 4 3
    7 4 2
    1 2 1
    */
    暴力出奇迹

    但其实这道题的正解是一个网络流(没想到吧?)

    让我们一起来分析一下它为什么是个网络流。

    我们发现,对于钦定边,只有比它小的可能会对其造成影响。而对于这些边,我们可以通过付出一些代价使得对钦定边不造成影响。

    或者我们可以这么想,对钦定边造成影响等价于什么呢?是不是没有连钦定边(端点为u,v)的时候,因为连了其他的一些边,使得u和v连通了。(right!)

    于是我们可以设u和v分别为源点S和汇点T,对于小于等于钦定边权值的边,就以它们的权值差+1作为流量,求最小割(因为要使得这些边无法让S和T连通)

    这种思想可谓是十分奇妙了~贴出代码

    #include<bits/stdc++.h>
    #define INF 2100000001
    #define M 300003
    #define N 100003
    #define LL long long
    using namespace std;
    int read()
    {
        int f=1,x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    struct EDGE{
        int x,y,z,flagg;
    }w[M];
    struct edgee{
        int to,nextt,val; 
    }e[M];
    int head[N],d[N],cur[N];
    int tot=1;
    int S,T;
    void add(int a,int b,int v)
    {
        tot++;
        e[tot].to=b;
        e[tot].nextt=head[a];
        e[tot].val=v;
        head[a]=tot;
        
        tot++;
        e[tot].to=a;
        e[tot].nextt=head[b];
        e[tot].val=0;
        head[b]=tot;
    }
    queue<int>q;
    int bfs()
    {
        memset(d,0,sizeof(d));
        while(!q.empty())q.pop();
        q.push(S);d[S]=1;
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=head[x];i;i=e[i].nextt)
            {
                int v=e[i].to;
                if(!d[v]&&e[i].val)
                {
                    d[v]=d[x]+1;
                    q.push(v);
                    if(v==T) return 1;
                }
            }
        }
        return 0;
    }
    int dinic(int x,int flow)
    {
        if(x==T) return flow;
        int rest=flow;
        int k;
        for(int i=head[x];i;i=e[i].nextt)
        {
            int v=e[i].to;
            if(e[i].val&&d[v]==d[x]+1)
            {
                k=dinic(v,min(rest,e[i].val));
                if(!k) d[v]=0;
                e[i].val-=k;
                e[i^1].val+=k;rest-=k;
            }
        }
        return flow-rest;
    }
    int main()
    {
        freopen("construct.in","r",stdin);
        freopen("construct.out","w",stdout);
        int n=read(),m=read(),id=read();
        for(int i=1;i<=m;++i)
        {
            w[i].x=read();w[i].y=read();w[i].z=read();
        }
        for(int i=1;i<=m;++i)
        {
            if(i==id)S=w[i].x,T=w[i].y;
            else if(w[i].z<=w[id].z) add(w[i].x,w[i].y,w[id].z-w[i].z+1),add(w[i].y,w[i].x,w[id].z-w[i].z+1);
        }
        int flow=0;LL maxflow=0;
        while(bfs())
        {
            while(flow=dinic(S,INF))
            maxflow+=flow;
        }
        printf("%lld
    ",maxflow);
    } 
    /*
    4 6 1
    1 2 2
    1 3 2
    1 4 3
    2 3 2
    2 4 4
    3 4 53 4 53 4 53 4 53
    */
    网络流打法
  • 相关阅读:
    H5,JS国际化网站中英文切换
    DIV右上角标签的CSS3实现技巧
    .NET Core也是国产化信息系统开发的重要选项
    微软自家的.Net下的JavaScript引擎--- ClearScript
    Java 生态碎片化 和 .NET生态的一致性
    使用 .NET 进行游戏开发
    .NET 是信息技术应用创新产业重要参与者
    Chrome 80+ 跨域Samesite 导致的cookie not found 解决方法
    在腾讯云云函数计算上部署.NET Core 3.1
    腾讯云 云开发 部署 Blazor网站
  • 原文地址:https://www.cnblogs.com/yyys-/p/11182213.html
Copyright © 2011-2022 走看看