【Codeforces 704D】Captain America
上下界费用流
题目大意:
给定二维平面上的 $n$ 个整点,要给每个整点涂上红、蓝两种颜色中的一种,每个点涂成红色、蓝色分别要花费 $r,b$ 元。
现在有 $m$ 个限制,每个限制会规定某行或某列中,红色点和蓝色点的个数的差不能超过 $d_i$。
问满足所有限制条件的最少花费并输出方案。或输出无解。
题解:
把行和列看成点,点看成边,则我们得到一张二分图。
我们假设所有点都涂成了红色。我们要使一个点从红色变成蓝色,其花费会增加 $b-r$。
考虑限制条件。对于某一行,假设它有 $n$ 个点,其限制为 $d$,其中蓝点的个数为 $k$,那么有:
也就是说,对每个有限制的行/列,其蓝点的个数都在一个限制范围内。
观察我们得到的二分图。我们需要选一些边,表示把这些边从红色改成蓝色。并使二分图上的每个点的度都在给定的限制内,还要使花费最小。
对二分图跑上下界最小费用可行流即可,由于是二分图,复杂度是正确的。输出方案只需要看哪些边被流过即可。
注意,如果 $b<r$,那么建完图后会存在负环。只需要把红色和蓝色交换一下即可。
无解的情况特判掉。
Code:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cstdlib>
using namespace std;
typedef long long LL;
const int N=5e5+6,inf=0x3f3f3f3f;
inline void failed(){cout<<"-1\n";exit(0);}
int n,m,Red,Blue,nod,head[N],cnt=1,dlt[N],X[N],Y[N],iter[N];
int Lx[N],Ly[N],Rx[N],Ry[N],Dx[N],Dy[N];
int degX[N],degY[N];
int S,T,TT;
int Id[N];
LL dis[N];
bool vis[N];
vector<int>vX,vY;
LL ans;
struct edge{
int to,nxt,cap,cost;
}e[N<<2];
inline int addedge(int u,int v,int cap,int cost){
e[++cnt]=(edge){v,head[u],cap,cost},head[u]=cnt;
e[++cnt]=(edge){u,head[v],0,-cost},head[v]=cnt;
return cnt-1;
}
inline void AddEdgE(int u,int v,int L,int R,int cost){
ans+=(LL)cost*L,dlt[u]-=L,dlt[v]+=L;addedge(u,v,R-L,cost);
}
int q[1048576];
int dfs(int u,int f){
if(u==TT||!f)return f;
vis[u]=1;
int ret=0;
for(int&i=iter[u];i;i=e[i].nxt)
if(e[i].cap&&!vis[e[i].to]&&dis[e[i].to]==dis[u]+e[i].cost){
int d=dfs(e[i].to,min(f,e[i].cap));
e[i].cap-=d,e[i^1].cap+=d,f-=d,ret+=d;
if(!f)break;
}
vis[u]=0;
return ret;
}
bool MCMF(){
memset(dis,0x3f,sizeof dis);
dis[0]=0;
int h=0,t=1;
q[0]=0;
while(h!=t){
int u=q[h++];
h&=1048575;
vis[u]=0;
for(int i=head[u];i;i=e[i].nxt)
if(e[i].cap&&dis[e[i].to]>dis[u]+e[i].cost){
dis[e[i].to]=dis[u]+e[i].cost;
if(!vis[e[i].to]){
vis[e[i].to]=1;
q[t++]=e[i].to;
t&=1048575;
}
}
}
if(dis[TT]>1e18)return 0;
memcpy(iter,head,sizeof head);
ans+=dfs(0,inf)*dis[TT];
return 1;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>Red>>Blue;
bool tag=0;
if(Red>Blue)swap(Red,Blue),tag=1;
for(int i=1;i<=n;++i){
cin>>X[i]>>Y[i];
vX.push_back(X[i]);
vY.push_back(Y[i]);
}
sort(vX.begin(),vX.end());
sort(vY.begin(),vY.end());
vX.erase(unique(vX.begin(),vX.end()),vX.end());
vY.erase(unique(vY.begin(),vY.end()),vY.end());
for(int i=0;i<vX.size();++i)Lx[i]=++nod,Rx[i]=++nod;
for(int i=0;i<vY.size();++i)Ly[i]=++nod,Ry[i]=++nod;
S=++nod,T=++nod,TT=++nod;
memset(Dx,0x3f,sizeof Dx);
memset(Dy,0x3f,sizeof Dy);
while(m--){
int t,x,d;
cin>>t>>x>>d;
if(t==1){
int id=lower_bound(vX.begin(),vX.end(),x)-vX.begin();
if(id<vX.size()&&vX[id]==x)Dx[id]=min(Dx[id],d);
}else{
int id=lower_bound(vY.begin(),vY.end(),x)-vY.begin();
if(id<vY.size()&&vY[id]==x)Dy[id]=min(Dy[id],d);
}
}
ans=(LL)Red*n;
for(int i=1;i<=n;++i){
int x=lower_bound(vX.begin(),vX.end(),X[i])-vX.begin();
int y=lower_bound(vY.begin(),vY.end(),Y[i])-vY.begin();
Id[i]=addedge(Rx[x],Ly[y],1,Blue-Red);
++degX[x],++degY[y];
}
for(int i=0;i<vX.size();++i){
addedge(S,Lx[i],n,0);
if(Dx[i]>1e9){
addedge(Lx[i],Rx[i],n,0);
continue;
}
int L=(degX[i]-Dx[i]+1)/2,R=(degX[i]+Dx[i])/2;
if(L>R)failed();
AddEdgE(Lx[i],Rx[i],L,R,0);
}
for(int i=0;i<vY.size();++i){
addedge(Ry[i],T,n,0);
if(Dy[i]>1e9){
addedge(Ly[i],Ry[i],n,0);
continue;
}
int L=(degY[i]-Dy[i]+1)/2,R=(degY[i]+Dy[i])/2;
if(L>R)failed();
AddEdgE(Ly[i],Ry[i],L,R,0);
}
addedge(T,S,inf,0);
for(int i=1;i<=T;++i)
if(dlt[i]>0)addedge(0,i,dlt[i],0);else
if(dlt[i]<0)addedge(i,TT,-dlt[i],0);
while(MCMF());
for(int i=head[0];i;i=e[i].nxt)
if(e[i].cap)failed();
cout<<ans<<'\n';
for(int i=1;i<=n;++i)
if(e[Id[i]].cap^tag)cout.put('r');else cout.put('b');
cout<<'\n';
return 0;
}