• Codeforces 1288F


    Codeforces 题面传送门 & 洛谷题面传送门

    好久没有写过上下界网络流了,先来一题再说(

    首先先假设所有边都是蓝边,那么这样首先就有 (b imes m) 的花费,但是这样不一定符合条件,就算符合条件也不一定是最优解,因此需要调整。

    显然一个点与其相连的边中,红边与蓝边的大小关系可用 (b-r) 来衡量,其中 (b,r) 分别表示与其相连的蓝边、红边的数量。我们考虑一个反悔贪心的思想,考虑将一条蓝边变成红边会对 (b-r) 产生怎样的影响,显然这可以分为两个阶段,一是蓝 ( o) 无的阶段,(b-r) 的值减一,而是无 ( o) 红的阶段,(b-r) 的值再次减一。因此考虑用两条边表示这两个阶段,一是 (u o v),容量 (1) 费用 (-b) 的边,表示蓝 ( o) 无的阶段,而是 (u o v),容量 (1) 费用 (r) 的边,表示无 ( o) 红的阶段。由于我们要求最小费用,因此对于两条重边,我们肯定会优先选费用小的走,即费用 (-b) 的边,符合我们的建图方式。而经我们这么一建图,一个点的 (b-r) 就很好表示了:就是该点的度减去经过该点的流的条数。接下来考虑一个点的限制,对于红点 (x),它的 (b-r)(<0),因此经过它的流的条数应 (>deg_x),因此如果它在左部,我们就连边 ((S,x,deg_x+1,2deg_x,0)),如果它在右部,我们就连边 ((x,T,deg_x+1,2deg_x,0))。对于蓝点 (x),它的 (b-r)(>0),因此经过它的流的条数应 (<deg_x),因此我们只用把上面连的边中 ((deg_x+1,2deg_x)) 全部换成 ((0,deg_x-1)) 即可。对于无色点显然没有任何限制,直接连 ((0,2deg_x)) 的边即可。然后跑最小费用可行流,答案就是费用 (+bm)

    直接跑会出现负环,因为在最小费用可行流中我们连了 (T o S) 的边,而这条边与中间我们连的费用为 (-b) 的边构成了大小为 (-b) 的环,因此需要消圈。具体消圈方法就是假设负权边满流,然后按照上下界网络流的套路建立虚拟源汇然后连相应的边调整流量即可。

    时间复杂度 (mathcal O( ext{能过}))

    const int MAXN=200;
    const int MAXV=404;
    const int MAXE=5000;
    int n1,n2,m,r,b,deg[MAXV+5];char s1[MAXN+5],s2[MAXN+5];
    int S1,S2,T1,T2,hd[MAXV+5],to[MAXE+5],nxt[MAXE+5],cst[MAXE+5],cap[MAXE+5],ec=1;
    void adde(int u,int v,int f,int c){
    	if(f<0) puts("-1"),exit(0);
    //	printf("%d %d %d %d
    ",u,v,f,c);
    	to[++ec]=v;cap[ec]=f;cst[ec]=c;nxt[ec]=hd[u];hd[u]=ec;
    	to[++ec]=u;cap[ec]=0;cst[ec]=-c;nxt[ec]=hd[v];hd[v]=ec;
    }
    int sum_has,sum_cst;
    void _adde(int u,int v,int l,int r,int c){
    	adde(u,v,r-l,c);adde(S2,v,l,0);adde(u,T2,l,0);
    	sum_has+=l;sum_cst+=1ll*l*c;
    }
    int dis[MAXV+5],lste[MAXV+5],flw[MAXV+5],pre[MAXV+5];
    bool inq[MAXV+5];
    bool getdis(int S,int T){
    	memset(dis,63,sizeof(dis));memset(flw,0,sizeof(flw));
    	dis[S]=0;flw[S]=INF;inq[S]=1;queue<int> q;q.push(S);
    	while(!q.empty()){
    		int x=q.front();q.pop();inq[x]=0;
    		for(int e=hd[x];e;e=nxt[e]){
    			int y=to[e],z=cap[e],w=cst[e];
    			if(z&&dis[y]>dis[x]+w){
    				dis[y]=dis[x]+w;flw[y]=min(flw[x],z);
    				pre[y]=x;lste[y]=e;
    				if(!inq[y]) inq[y]=1,q.push(y);
    			}
    		}
    	} return dis[T]<INF;
    }
    pair<int,int> mcmf(int S,int T){
    	int mxfl=0,mncst=0;
    	while(getdis(S,T)){
    		mncst+=1ll*flw[T]*dis[T];mxfl+=flw[T];
    		for(int i=T;i^S;i=pre[i]){
    			cap[lste[i]]-=flw[T];cap[lste[i]^1]+=flw[T];
    		}
    	} return mp(mxfl,mncst);
    }
    int main(){
    	scanf("%d%d%d%d%d%s%s",&n1,&n2,&m,&r,&b,s1+1,s2+1);
    	T2=(S2=(T1=(S1=n1+n2+1)+1)+1)+1;
    	for(int i=1,u,v;i<=m;i++){
    		scanf("%d%d",&u,&v);v+=n1;
    		deg[u]++;deg[v]++;adde(v,u,1,b);
    		adde(S2,v,1,0);adde(u,T2,1,0);
    		sum_cst-=b;sum_has++;adde(u,v,1,r);
    	}
    	for(int i=1;i<=n1;i++){
    		if(s1[i]=='R') _adde(S1,i,deg[i]+1,deg[i]*2,0);
    		if(s1[i]=='B') adde(S1,i,deg[i]-1,0);
    		if(s1[i]=='U') adde(S1,i,deg[i]*2,0);
    	}
    	for(int i=1;i<=n2;i++){
    		if(s2[i]=='R') _adde(i+n1,T1,deg[i+n1]+1,deg[i+n1]*2,0);
    		if(s2[i]=='B') adde(i+n1,T1,deg[i+n1]-1,0);
    		if(s2[i]=='U') adde(i+n1,T1,deg[i+n1]*2,0);
    	} adde(T1,S1,INF,0);
    	pair<int,int> pr=mcmf(S2,T2);
    	if(pr.fi!=sum_has) return puts("-1"),0;
    	int res=sum_cst+pr.se+m*b;
    	printf("%d
    ",res);
    	for(int i=3,j=1;j<=m;i+=8,j++){
    		if(cap[i]==1) putchar('B');
    		else if(cap[i+6]==1) putchar('R');
    		else putchar('U');
    	}
    	return 0;
    }
    
  • 相关阅读:
    查找——图文翔解HashTree(哈希树)
    比較JS合并数组的各种方法及其优劣
    PTA 是否同一棵二叉搜索树(25 分)
    PTA 最大子列和问题(10 分)
    PTA PAT排名汇总(25 分)
    PTA PAT排名汇总(25 分)
    PTA 说反话-加强版(20 分)(字符串处理)
    PTA 说反话-加强版(20 分)(字符串处理)
    Tarjan模版(链式向前星表示方法)
    Tarjan模版(链式向前星表示方法)
  • 原文地址:https://www.cnblogs.com/ET2006/p/Codeforces-1288F.html
Copyright © 2020-2023  润新知