P5295 [北京省选集训2019]图的难题 最大权闭合子图

题意:

戳这里

分析:

  • 前置 (Lemma) :

    一个图是森林的充要条件是对于每一个子图都满足 (|E|le 2|V|-2)

  • 证明:

    必要性:一个图在极端情况下会分成两个树,此时任何一条边都会使一个树变成基环树

    充分性:如果一个子图不满足次性质,那么该子图一定带环,不是森林


我们按照上面的定理,发现边的价值为 1,点的价值为 -2,选一条边必须选一个点,如果一个子图的权值大于 -2 那么不是森林

对于带依赖关系选出权值和最大这种条件,我们很容易联想到最大权闭合子图

我们建出图之后发现,有的情况下选出来的权值和为 -1,但不选的最大和值可能是 0,所以我们得强制有的点必选,这样直接做的复杂度是 (O(tn^3m)) 复杂度过高,我们发现每次强制选点和之前的图相比最多改变了两条边,所以我们采取退流的做法,这样将复杂度降到了 (O(tn^2m))

代码:

#include<bits/stdc++.h>
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
    inl int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
        return x*f;
    }

    const int maxn = 6005;
    const int maxm = 5e5+5;
    const int inf = 1e9+7;
    int n,m,cnt=1;
    int head[maxn],id[maxn],cur[maxn],dep[maxn];
    queue<int> q;
    struct edge
    {
        int to,nxt,w;
    }e[maxm];

    inl void add(int u,int v,int w)
    {
        e[++cnt].to=v;
        e[cnt].w=w;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }

    inl void add_edge(int u,int v,int w)
    {
        add(u,v,w);add(v,u,0);
    }
    
    inl bool bfs(int st,int ed)
    {
        for(reg int i=0;i<=n+m+1;i++) dep[i]=-1;
        q.push(st);dep[st]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(reg int i=head[u];i;i=e[i].nxt)
            {
                int v=e[i].to;
                if(e[i].w&&dep[v]==-1)
                {
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        return dep[ed]!=-1;
    }

    int dfs(int u,int ed,int flow)
    {
        if(u==ed||!flow) return flow;
        int used=0,w;
        for(reg int &i=cur[u];i;i=e[i].nxt)
        {
            int v=e[i].to;
            if(e[i].w&&dep[v]==dep[u]+1)
            {
                w=dfs(v,ed,min(e[i].w,flow-used));
                e[i].w-=w;
                e[i^1].w+=w;
                used+=w;
                if(used==flow) return flow;
            }
        }
        if(!used) dep[u]=-1;
        return used;
    }

    inl int dinic(int st,int ed,int f=inf)
    {
        int res=0;
        while(f>res&&bfs(st,ed))
        {
            memcpy(cur,head,sizeof(head));
            res+=dfs(st,ed,f-res);
        }
        return res;
    }

    void work()
    {
		cnt=1;memset(head,0,sizeof(head));
    	n=read();m=read();
        int st=0,ed=n+m+1;
   		for(reg int i=1;i<=m;i++)
    	{
        	add_edge(n+i,read(),inf);
        	add_edge(n+i,read(),inf);
        	add_edge(st,n+i,1);
   		}
    	for(reg int i=1;i<=n;i++) add_edge(i,ed,2),id[i]=cnt;
    	int ans=dinic(st,ed);
    	for(reg int i=1;i<=n;i++)
    	{
        	int flow=e[id[i]].w;
        	e[id[i]].w=e[id[i]^1].w=0;
        	if(flow) ans-=dinic(i,st,flow);
        	if(i>1) e[id[i-1]^1].w=2;
        	ans+=dinic(st,ed);
            if(m-ans>0)
            {
                puts("No");
                return ;
            }
    	}
    	puts("Yes");	
    }

}

int main()
{
	int t;cin>>t;
	while(t--) zzc::work();
    return 0;
}
原文地址:https://www.cnblogs.com/youth518/p/14488005.html