zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    字符串操作函数5!!
    字符串操作函数4
    字符串操作函数3
    java开发命名规范总结
    centerOS网络NAT和桥接
    input框的内容变化监听
    Xunsearch迅搜项目实战经验
    PHP网络爬虫之CURL学习
    Xunsearch的使用总结
    Xshell 5 过期
  • 原文地址:https://www.cnblogs.com/youth518/p/14488005.html
Copyright © 2011-2022 走看看