• 【CF704D】Captain America(上下界网络流)


    点此看题面

    • 平面上有(n)个点,你需要把每个点染色,每染一个红点需要(r)的代价,每染一个蓝点需要(b)的代价。
    • (m)个限制,形如直线(x=i/y=i)上两种颜色点数差不能超过(d)
    • 求构造一种染色方案,使得代价最小。
    • (n,mle10^5)

    网络流模型

    不妨假设(rle b),那么我们肯定尽可能选择染红色。

    对于一行,假设它有(c)个点,最紧的限制为(s),那么就相当于可以选择(lceilfrac {c-s}2 ceilsimlfloorfrac{c+s}2 floor)个红点。

    由于每一个点有两个坐标,它受到二重约束。

    因此我们可以在靠近源点的方向为(x=i)的限制建一排点,在靠近汇点的方向为(y=i)的限制建一排点,这些点分别与源点和汇点连边限制流量,而平面上的每个点((x_0,y_0))都可以看作从左侧(x=x_0)的点连向右侧(y=y_0)的点的一条容量为(1)的边。

    这样一来就建立起一个网络流模型,而且这是一张二分图。

    有源汇有上下界最大流

    注意到这里的有些边是有下界的限制的。

    解决方案可参照这道板子题:【LOJ116】有源汇有上下界最大流

    最后要输出方案,只要看点对应的边是否被流过,被流过(剩余流量为(0))说明是红色。

    代码:(O(nsqrt n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define INF (int)1e9 
    #define NA() (puts("-1"),exit(0),0)
    using namespace std;
    int n,m,r,b,op,xx[N+5],yy[N+5],cx[N+5],cy[N+5],sx[N+5],sy[N+5],d[2*N+5];
    int tx;map<int,int> wx;I int px(CI x) {return wx.count(x)?wx[x]:wx[x]=++tx;}
    int ty;map<int,int> wy;I int py(CI y) {return wy.count(y)?wy[y]:wy[y]=++ty;}
    namespace D//有源汇有上下界网络流
    {
    	#define PS (2*N+4)
    	#define ES (3*N)
    	#define s0 (tx+ty+1)
    	#define t0 (tx+ty+2)
    	#define ss (tx+ty+3)
    	#define tt (tx+ty+4)
    	#define adde(x,y,f) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].F=f)
    	int ee=1,lnk[PS+5],cur[PS+5];struct edge {int to,nxt,F;}e[2*ES+5];
    	I void Add(CI x,CI y,CI f) {adde(x,y,f),adde(y,x,0);}
    	int q[PS+5],d[PS+5];I bool BFS(CI s,CI t)
    	{
    		RI i,k,H,T;for(i=1;i<=tt;++i) d[i]=0;d[q[H=T=1]=s]=1;W(H<=T&&!d[t])
    			for(i=lnk[k=q[H++]];i;i=e[i].nxt) !d[e[i].to]&&e[i].F&&(d[q[++T]=e[i].to]=d[k]+1);
    		return memcpy(cur,lnk,sizeof(lnk)),d[t];
    	}
    	I int DFS(CI x,CI t,RI f=INF)
    	{
    		if(x==t||!f) return f;RI i,o,g=0;for(i=cur[x];i;i=e[i].nxt)
    		{
    			if((d[x]+1)^d[e[i].to]||!e[i].F||!(o=DFS(e[i].to,t,min(f,e[i].F)))) continue;
    			if(e[i].F-=o,e[i^1].F+=o,g+=o,!(f-=o)) break;
    		}return cur[x]=i,g;
    	}
    	I bool Try() {W(BFS(ss,tt)) DFS(ss,tt);for(RI i=lnk[ss];i;i=e[i].nxt) if(e[i].F) return 0;return 1;}//用虚拟源汇跑可行流
    	I int MaxFlow() {RI g=0;W(BFS(s0,t0)) g+=DFS(s0,t0);return g;}//用实际源汇跑最大流
    	I void Print() {for(RI i=1;i<=n;++i) putchar(e[i<<1].F^op?'b':'r');}//根据是否有流量剩余判断颜色
    }
    int main()
    {
    	RI i,x,y;scanf("%d%d%d%d",&n,&m,&r,&b),r>b&&(swap(r,b),op=1);
    	for(i=1;i<=n;++i) scanf("%d%d",xx+i,yy+i),++cx[xx[i]=px(xx[i])],++cy[yy[i]=py(yy[i])];//统计每条直线上的点数
    	for(i=1;i<=tx;++i) sx[i]=cx[i];for(i=1;i<=ty;++i) sy[i]=cy[i];for(i=1;i<=n;++i) D::Add(xx[i],tx+yy[i],1);//把每个点变成两条直线对应点间的连边
    	RI op;for(i=1;i<=m;++i) scanf("%d%d%d",&op,&x,&y),
    		op==1?wx.count(x)&&(sx[wx[x]]=min(sx[wx[x]],y)):wy.count(x)&&(sy[wy[x]]=min(sy[wy[x]],y));//记录限制
    	for(i=1;i<=tx;++i) (x=cx[i]-sx[i]+1>>1)>(y=cx[i]+sx[i]>>1)?NA():(D::Add(s0,i,y-x),d[s0]-=x,d[i]+=x);//建边有上下界
    	for(i=1;i<=ty;++i) (x=cy[i]-sy[i]+1>>1)>(y=cy[i]+sy[i]>>1)?NA():(D::Add(tx+i,t0,y-x),d[tx+i]-=x,d[t0]+=x);//建边有上下界
    	for(i=1;i<=t0;++i) d[i]>0&&(D::Add(ss,i,d[i]),0),d[i]<0&&(D::Add(i,tt,-d[i]),0);D::Add(t0,s0,INF);//根据强制度数与源汇连边
    	return D::Try()?(printf("%lld
    ",1LL*D::MaxFlow()*(r-b)+1LL*n*b),D::Print(),0):puts("-1"),0;//上下界网络流
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    娓娓道来c指针 (4)解析c的声明语句
    Snail—UI学习之UITextField
    E
    Qt录音机
    著名的英文搜索引擎
    java中Map,List与Set的差别
    Android图片处理:识别图像方向并显示
    Unity3D中组件事件函数的运行顺序
    Android屏幕density, dip等相关概念总结
    Codeforces Round #257 (Div. 2)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF704D.html
Copyright © 2020-2023  润新知