zoukankan      html  css  js  c++  java
  • [jzoj 4528] [GDOI2019模拟2019.3.26] 要换换名字 (最大权闭合子图)

    题目链接:

    https://jzoj.net/senior/#contest/show/2683/0

    题目:

    题解:

    不妨枚举一个点,让两颗树都以这个点为根,求联通块要么点数为$0$,要么包括根(即联通块必须从根开始)

    考虑一个不是根的点$x$,若$x$在联通块以内,要保证联通块的连通性,那么从$x$的父亲要在联通块以内

    这种一个点选了就必须要选另一个点的问题是典型的最大权闭合子图模型

    做法如下

    设$s$为源点,$t$为汇点。

    使$s$连向所有的正权点(非负权点),边权为点权

    使所有非负权点(负权点)连向$t$,边权为点权的绝对值

    若需要选$y$就要选$x$,连一条由$x$到$y$的边,边权是$∞$(其实这个地方我一直有疑惑,有的认为是连从$x$到$y$的边,如果有读者知道真相麻烦告诉我)

    最大点权和=正权点和-最小割

    证明:
    我们知道一个割会把图分成两个部分,一部分是$S$能走到的,另一部分是能走到$T$的。
    我们设这两个集合为$A$,$B$。

    $A0$,$B0$分别表示$A$,$B$中原来点权是负数的点集, $A1$,$B1$分别表示$A$,$B$中点权原来是正数的点集。

    由于中间的边是$∞$的,割的一定是和$S$,$T$相连的边,这个叫简单割。
    所以割的大小 $= |A0| +B1$ 

    $A,B$一定是最大权闭合子图
    $A$的点权和 $= A1 - |A0|$ 
    $A$的点权和+割的大小$=A1 + B1 = $全图中的正权点和
    这是一个定值, 要使$A$的点权和最大, 就要使割最小,就是最小割,

    至此得证。

    代码:

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<queue>
    using namespace std;
    
    const int N=600+15; 
    const int inf=1e9+7;
    int n,tot1,tot2,cnt;
    int a[N],h1[N],h2[N],head[N],dep[N],nowhead[N];
    struct EDGE
    {
        int to,nxt;
    }e1[N<<1],e2[N<<1];
    struct node
    {
        int to,nxt;
        int flow;
    }edge[N<<2];
    inline int read()
    {
        char ch=getchar();int s=0,f=1;
        while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    void link(int u,int v,int w)
    {
        edge[++cnt]=(node){v,head[u],w};head[u]=cnt;
        edge[++cnt]=(node){u,head[v],0};head[v]=cnt;
    }
    void add1(int u,int v)
    {
        e1[++tot1]=(EDGE){v,h1[u]};
        h1[u]=tot1;
    }
    void add2(int u,int v)
    {
        e2[++tot2]=(EDGE){v,h2[u]};
        h2[u]=tot2;
    }
    void init()
    {
        cnt=1;
        memset(head,-1,sizeof(head));
    }
    void dfs1(int x,int pre)
    {
        if (pre) link(x,pre,inf);
        for (int i=h1[x];i;i=e1[i].nxt)
        {
            int y=e1[i].to;
            if (y==pre) continue;
            dfs1(y,x);
        }
    }
    void dfs2(int x,int pre)
    {
        if (pre) link(x,pre,inf);
        for (int i=h2[x];i;i=e2[i].nxt)
        {
            int y=e2[i].to;
            if (y==pre) continue;
            dfs2(y,x);
        }
    }
    int S,T;
    queue <int> q;
    int bfs()
    {
        memset(dep,0,sizeof(dep));
        while (!q.empty()) q.pop(); 
        dep[S]=1;
        q.push(S);
        while (!q.empty())
        {
            int k=q.front();q.pop();
            for (int i=head[k];i!=-1;i=edge[i].nxt)
            if (edge[i].flow&&!dep[edge[i].to])
            {
                dep[edge[i].to]=dep[k]+1;
                q.push(edge[i].to);
            }
        }
        return dep[T];
    }
    int dfs(int x,int a)
    {
        if (x==T||!a) return a;
        int flow=0,f;
        for (int &i=nowhead[x];i!=-1;i=edge[i].nxt)
        if (dep[x]+1==dep[edge[i].to]&&(f=dfs(edge[i].to,min(a,edge[i].flow)))>0)
        {
            edge[i].flow-=f;
            edge[i^1].flow+=f;
            flow+=f;
            a-=f;
            if (a==0) break;
        }
        return flow;
    }
    int dinic()
    {
        int ans=0;
        while (bfs())
        {    
            copy(head,head+n+3,nowhead);//这行优化重要无比 
            ans+=dfs(S,inf);
        } 
        return ans;
    }
    int main()
    {
        //freopen("theory.in","r",stdin);
        int W=read();
        while (W--)
        {
            tot1=0;tot2=0;
            int sum=0;
            n=read();
            S=n+1,T=n+2;
            for (int i=1;i<=n;i++) 
            {
                h1[i]=0;h2[i]=0;
                a[i]=read();
                if (a[i]>0) sum+=a[i];
            }
            for (int i=1;i<n;i++)
            {
                int u=read(),v=read();
                add1(u,v);add1(v,u);
            }
            for (int i=1;i<n;i++)
            {
                int u=read(),v=read();
                add2(u,v);add2(v,u);
            }
            int res=0;
            for (int i=1;i<=n;i++)
            {
                init();
                dfs1(i,0);dfs2(i,0);
                for (int j=1;j<=n;j++) if (a[j]>=0) link(S,j,a[j]);else link(j,T,-a[j]);
                res=max(res,sum-dinic());
            }
            printf("%d
    ",res);
        }
        return 0;
    }
  • 相关阅读:
    BZOJ 1050 旅行
    BZOJ 1040 骑士
    BZOJ 1038 瞭望塔
    BZOJ 1037 生日聚会
    BZOJ 1823 满汉全席
    BZOJ 3091 城市旅行
    CF702E Analysis of Pathes in Functional Graph
    Luogu 2154 [SDOI2009]虔诚的墓主人
    Luogu 1268 树的重量
    Luogu 4867 Gty的二逼妹子序列
  • 原文地址:https://www.cnblogs.com/xxzh/p/10604083.html
Copyright © 2011-2022 走看看