2018.10.11 bzoj3894: 文理分科(最小割)

传送门
最小割经典题目。


题目让我们求最大的贡献。
那我们先考虑加上全部的贡献,然后求出最小的需要丢掉的贡献。
那么这个时候,如果没有限制就已经是最小割的典型模型了。
即直接建边 ( s , p o s i , j , a r t i , j ) , ( p o s i , j , t , a r t i , j ) (s,pos_{i,j},art_{i,j}),(pos_{i,j},t,art_{i,j}) (s,posi,j,arti,j),(posi,j,t,arti,j)
这样求最小割是正确的。
现在有了满意值,怎么做呢?
直接分类讨论就行了。

  1. 学文且周围的学文,会损失 s c i e n c e + s a m e science+same science+same_ s c i e n c e science science那么多贡献。
  2. 学文且周围的学理,会损失 s a m e same same _ a r t + s c i e n c e + s a m e art+science+same art+science+same_ s c i e n c e science science那么多贡献。
  3. 学文且周围的不全学文,会损失 s a m e same same _ a r t + s c i e n c e + s a m e art+science+same art+science+same_ s c i e n c e science science那么多贡献。

学理是同理的。
按照这个来建边就行了:
( s , p o s i , j ′ , s a m e (s,pos'_{i,j},same (s,posi,j,same_ a r t i , j ) , ( p o s i , j ′ , p o s i ′ , j ′ , i n f ) art_{i,j}),(pos'_{i,j},pos_{i',j'},inf) arti,j),(posi,j,posi,j,inf),其中 ( i , j ) , ( i ′ , j ′ ) (i,j),(i',j') (i,j),(i,j)是相邻的。
理科同理。
这样子割出来一定是对的。
可以画个图来分类验证。
代码:

#include
#define M 1000005
#define N 40005
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,m,tim[505][505],Clocks=0,ans=0,dx[5]={0,0,0,-1,1},dy[5]={0,-1,1,0,0};
struct edge{int v,next,c;};
struct Dinic{
	edge e[M<<1];
	int first[N],cur[N],cnt,s,t,d[N];
	inline void addedge(int u,int v,int c){e[++cnt].v=v,e[cnt].c=c,e[cnt].next=first[u],first[u]=cnt;}
	inline void add(int u,int v,int c){addedge(u,v,c),addedge(v,u,0);}
	inline void init(){memset(first,-1,sizeof(first)),cnt=-1,s=0,t=n*m*3+1;}
	inline bool bfs(){
		queue<int>q;
		for(int i=1;i<=t;++i)d[i]=-1;
		q.push(s);
		while(!q.empty()){
			int x=q.front();
			q.pop();
			for(int i=first[x];~i;i=e[i].next){
				int v=e[i].v;
				if(~d[v]||!e[i].c)continue;
				d[v]=d[x]+1,q.push(v);
			}
		}
		return ~d[t];
	}
	inline int dfs(int x,int f){
		if(x==t||!f)return f;
		int flow=f;
		for(int&i=cur[x];~i;i=e[i].next){
			int v=e[i].v;
			if(!flow)break;
			if(e[i].c&&d[v]==d[x]+1){
				int tmp=dfs(v,min(flow,e[i].c));
				if(!tmp)d[v]=-1;
				e[i].c-=tmp,e[i^1].c+=tmp,flow-=tmp;
			}
		}
		return f-flow;
	}
	inline int solve(){
		int ret=0;
		while(bfs())memcpy(cur,first,sizeof(first)),ret+=dfs(s,0x3f3f3f3f);
		return ret;
	}
}dinic;
int main(){
	n=read(),m=read(),dinic.init();
	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)tim[i][j]=++Clocks;
	for(int i=1;i<=n;i++)for(int val,j=1;j<=m;++j)val=read(),dinic.add(dinic.s,tim[i][j],val),ans+=val;
	for(int i=1;i<=n;++i)for(int val,j=1;j<=m;++j)val=read(),dinic.add(tim[i][j],dinic.t,val),ans+=val;
	for(int i=1;i<=n;++i)for(int val,j=1;j<=m;++j){
		val=read(),dinic.add(dinic.s,tim[i][j]+n*m,val),ans+=val;
		for(int k=0;k<=4;++k){
			int mx=dx[k]+i,my=dy[k]+j;
			if(!mx||!my||mx>n||my>m)continue;
			dinic.add(tim[i][j]+n*m,tim[mx][my],0x3f3f3f3f);
		}
	}
	for(int i=1;i<=n;++i)for(int val,j=1;j<=m;++j){
		val=read(),dinic.add(tim[i][j]+2*n*m,dinic.t,val),ans+=val;
		for(int k=0;k<=4;++k){
			int mx=dx[k]+i,my=dy[k]+j;
			if(!mx||!my||mx>n||my>m)continue;
			dinic.add(tim[mx][my],tim[i][j]+2*n*m,0x3f3f3f3f);
		}
	}
	cout<<ans-dinic.solve();
	return 0;
}

你可能感兴趣的:(#,最大流)