P3153 [CQOI2009]跳舞

传送门

考虑二分答案
把男女生都拆点,拆成喜欢和不喜欢
\(S\)向男生喜欢连边,容量\(mid\),男生喜欢向不喜欢连边,容量\(k\),女生同理
然后男生喜欢向女生喜欢连边,男生不喜欢向女生不喜欢连边
然后跑一遍最大流看是否满流即可

//minamoto
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define R register
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(R int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(R int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
const int N=1005,M=100005;
struct eg{int v,nx,w;}e[M];int head[N],tot=1;
inline void add(R int u,R int v,R int w){
	e[++tot]={v,head[u],w},head[u]=tot;
	e[++tot]={u,head[v],0},head[v]=tot;
}
int n,dep[N],cur[N],S,T,q[N],hh[N],res,k,h,t,l,r,mid,ans;char s[N];
bool bfs(){
	fp(i,S,T)cur[i]=head[i],dep[i]=-1;
	q[h=t=1]=S,dep[S]=0;
	while(h<=t){
		int u=q[h++];
		go(u)if(e[i].w&&dep[v]<0){
			dep[v]=dep[u]+1,q[++t]=v;
			if(v==T)return true;
		}
	}return false;
}
int dfs(int u,int lim){
	if(u==T||!lim)return lim;
	int flow=0,f;
	gg(u)if(dep[v]==dep[u]+1&&(f=dfs(v,min(lim,e[i].w)))){
		flow+=f,lim-=f;
		e[i].w-=f,e[i^1].w+=f;
		if(!lim)break;
	}return flow;
}
int dinic(){int flow=0;while(bfs())flow+=dfs(S,inf);return flow;}
bool check(){
	fp(i,S,T)head[i]=hh[i];tot=res;for(R int i=2;i<=tot;i+=2)e[i].w+=e[i^1].w,e[i^1].w=0;
	fp(i,1,n)add(S,i,mid);fp(i,1,n)add(i+2*n,T,mid);
	return dinic()==mid*n;
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&k),S=0,T=4*n+1;
	fp(i,1,n){
		scanf("%s",s+1);
		fp(j,1,n)s[j]=='Y'?add(i,j+2*n,1):add(i+n,j+3*n,1);
	}if(k){
		fp(i,1,n)add(i,i+n,k);fp(i,1,n)add(i+3*n,i+2*n,k);
	}fp(i,S,T)hh[i]=head[i];res=tot;
	l=0,r=n;
	while(l<=r){
		mid=(l+r)>>1;
		check()?(ans=mid,l=mid+1):(r=mid-1);
	}printf("%d\n",ans);return 0;
}
posted @ 2018-12-05 12:48  bztMinamoto  阅读(154)  评论(0编辑  收藏  举报
Live2D