zoukankan      html  css  js  c++  java
  • 6478. 【GDOI2020模拟02.19】C(上下界费用流、费用流消负环)

    题目描述


    有源汇上下界最小费用最大流

    同上下界网络流,把u->v拆成S'->v、u->v和u->T'三条边,其中S'->v和u->v有代价

    也可以求出每个点的出入情况D[i],表示(流入-流出)

    如果D[i]>0则连S'->i,否则连i->T'

    本质是强制必经边满流,然后再平衡流量

    然后跑S'->T'求出可行流,再跑原S->T求出最大流

    费用流消负环

    可以用消圈算法,但是很慢,考虑用上面的"退流"思想

    新建源汇点S''和T'',若一条边的代价<0则强制将其满流,计入答案的同时求出每个点的出入情况E[i]

    如果E[i]>0则连S''->i,否则连i->T''

    然后跑S''->T''的最小费用最大流即可消掉负环

    一点思(kou)考(hu)

    当一个费用流图无负环时,增广一次后为什么不会出现负环?

    这是跑完之后的图,假设中间的是负环

    若出现负环(跑完之后出现),则说明增广路径上存在uv两点,使得路径上u->v的距离a和不经过增广路径的距离b满足b-a<0

    也就是b<a,那么必然会跑更短的路径,所以矛盾

    更复杂的情况同理

    code

    #include <bits/stdc++.h>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define ll long long
    #define file
    using namespace std;
    
    struct tree{
    	int a[701][2],ls[161],s[161],S[161],len,i,j,k,l;
    	
    	void New(int x,int y) {++len;a[len][0]=y;a[len][1]=ls[x];ls[x]=len;}
    	void dfs(int Fa,int t)
    	{
    		int i;
    		
    		for (i=ls[t]; i; i=a[i][1])
    		if (a[i][0]!=Fa)
    		{
    			s[a[i][0]]=s[a[i][0]]+s[t];
    			dfs(t,a[i][0]);
    			S[t]+=S[a[i][0]];
    		}
    	}
    } tr;
    int a[3001][4],A[161],B[161],A2[161],B2[161],ls[161],fa[161],S[161],T[161],pa[161],pb[161],
    f[161],g[161],G[161],d[1000001],D[161],E[161],n,m,i,j,k,l,len,st,ed,ans,h,t,sum,St,Ed;
    bool bz[161];
    
    void New(int x,int y,int f,int w)
    {
    	++len;
    	a[len][0]=y;
    	a[len][1]=ls[x];
    	ls[x]=len;
    	a[len][2]=f;
    	a[len][3]=w;
    }
    void NEW(int x,int y,int f,int w)
    {
    	if (!f) return;
    	
    	if (w<0)
    	{
    		ans+=f*w,E[y]+=f,E[x]-=f;
    		New(x,y,0,w);
    		New(y,x,f,-w);
    		
    		return;
    	}
    	
    	New(x,y,f,w);
    	New(y,x,0,-w);
    }
    void add(int x,int y,int A,int B,int w)
    {
    	if (A>B)
    	{
    		printf("-1
    ");
    		exit(0);
    	}
    	
    	ans+=A*w;
    	D[y]+=A,D[x]-=A;
    	NEW(x,y,B-A,w);
    }
    
    void work(int st,int ed)
    {
    	int i,j,k,l;
    	
    	while (1)
    	{
    		h=0;t=1;
    		d[1]=st;
    		memset(f,127,sizeof(f));
    		f[st]=0;
    		bz[st]=1;
    		
    		while (h<t)
    		{
    			for (i=ls[d[++h]]; i; i=a[i][1])
    			if (a[i][2] && f[d[h]]+a[i][3]<f[a[i][0]])
    			{
    				f[a[i][0]]=f[d[h]]+a[i][3];
    				g[a[i][0]]=d[h];
    				G[a[i][0]]=i;
    				
    				if (!bz[a[i][0]] && a[i][0]!=ed)
    				d[++t]=a[i][0],bz[a[i][0]]=1;
    			}
    			
    			bz[d[h]]=0;
    		}
    		if (f[ed]>2000000000) break;
    		
    		sum=2000000000;
    		for (i=ed; i!=st; i=g[i])
    		sum=min(sum,a[G[i]][2]);
    		
    		ans+=sum*f[ed];
    		for (i=ed; i!=st; i=g[i])
    		a[G[i]][2]-=sum,a[G[i]^1][2]+=sum;
    	}
    }
    
    int main()
    {
    	freopen("C.in","r",stdin);
    	#ifdef file
    	freopen("C.out","w",stdout);
    	#endif
    	
    	scanf("%d%d",&n,&m);len=1;st=n+3,ed=n+4;St=n+5,Ed=n+6;
    	fo(i,2,n) scanf("%d%d%d%d%d",&fa[i],&A[i],&B[i],&A2[i],&B2[i]),tr.New(fa[i],i),tr.s[i]=B2[i];
    	fo(i,1,m)
    	{
    		scanf("%d%d%d%d",&S[i],&T[i],&pa[i],&pb[i]);
    		++tr.S[T[i]];--tr.S[S[i]];
    	}
    	
    	tr.dfs(0,1);
    	
    //	---
    	
    	fo(i,2,n)
    	add(i,fa[i],max(0,tr.S[i]-B[i]),min(A[i],tr.S[i]),A2[i]);
    	fo(i,1,m)
    	ans+=pa[i],add(n+1,T[i],0,1,0),add(S[i],n+2,0,1,0),add(T[i],S[i],0,1,pb[i]-pa[i]+(tr.s[T[i]]-tr.s[S[i]]));
    	
    	k=len;
    	add(n+2,n+1,0,2133333333,0);
    	fo(i,1,Ed) if (D[i]>0) NEW(st,i,D[i],0); else if (D[i]<0) NEW(i,ed,-D[i],0); //上下界
    	l=len;
    	fo(i,1,ed) if (E[i]>0) NEW(St,i,E[i],0); else if (E[i]<0) NEW(i,Ed,-E[i],0); //负环
    	
    //	---
    	
    	work(St,Ed); //负环
    	
    	fo(i,1,ed)
    	while (ls[i]>l)
    	ls[i]=a[ls[i]][1];
    	work(st,ed); //上下界
    	
    	fo(i,1,n+2)
    	while (ls[i]>k)
    	ls[i]=a[ls[i]][1];
    	
    	work(n+1,n+2); //最大流
    	
    //	---
    	
    	for (i=ls[st]; i; i=a[i][1])
    	if (a[i][2])
    	{
    		printf("-1
    ");
    		return 0;
    	}
    	printf("%d
    ",ans);
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    

    参考资料

    https://www.cnblogs.com/leason-lyx/p/11144527.html
    https://www.cnblogs.com/Miracevin/p/10028021.html
    https://www.itdaan.com/blog/2012/11/20/e12975f2853632e9c471b1b375b5cac7.html

  • 相关阅读:
    系统调用的三层机制(上)
    深入理解计算机系统——第二章学习笔记
    MenOS
    操作系统工作流程
    从问题到程序——第一二章学习笔记
    2018-2019-1 20189210 《LInux内核原理与分析》第六周作业
    2018-2019-1 20189210 《LInux内核原理与分析》第五周作业
    2018-2019-1 20189210 《LInux内核原理与分析》第四周作业
    2018-2019-1 20189210 《Linux内核原理与分析》第三周作业
    20189210牟健 《Linux内核原理与分析》第二周作业
  • 原文地址:https://www.cnblogs.com/gmh77/p/12360139.html
Copyright © 2011-2022 走看看