Description

【GDKOI2012模拟02.01】矩阵_GDKOI模拟

Solution

其实这是一道很套路的题目。
首先,我们知道最后的答案之和每行每列的和的最大值有关。
那么我们可以考虑二分这个最大值然后在判断可行解。
我们现在已经知道了最大值,那么要分配方案的话,那么很明显就是上下界网络流了。
源点对每行连h[i]-mid为下界,h[i]+mid为上界。
每行向每列连上下界为[L,R]的边
每列向汇点连上下界为[l[i]-mid,l[i]+mid]的边
然后判断是否有可行流就好了:SS连出去的边是否满流
然后就没了。

可能会超时

但是有些人打的不好会超时,那么该怎么办。
首先其实二分的时候可以不用网络流来搞,可以二分的时候把每行每列的范围加起来,然后判断行和列的范围时候有交集,如果有交集那么就说明可行。
还有一个优化,就是用zkw来跑,这样可以跑得超快。

Code

#include<iostream> 
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=3e5+7,inf=0x7fffffff;
int i,j,k,l,t,n,m,ans,S,T,x,SS,TT,L,R,r,mid,o,yi,er,yy,ee;
int first[maxn],last[maxn],next[maxn],chang[maxn],fan[maxn],num;
int d[1000],data[maxn],hang[maxn],lie[maxn],an[207][207];
void add(int x,int y,int z){
last[++num]=y,next[num]=first[x],first[x]=num,chang[num]=z;fan[num]=num+1;
last[++num]=x,next[num]=first[y],first[y]=num,chang[num]=0;fan[num]=num-1;
}
void link(int x,int y,int r,int l){
add(x,y,r-l);add(SS,y,l);o+=l;
add(x,TT,l);
}
int dinic(int x,int y){
if(x==TT)return y;
int i,j,k=0;
rep(i,x){
if(chang[i]&&d[last[i]]==d[x]+1){
j=dinic(last[i],min(y,chang[i]));
if(j){
chang[i]-=j;chang[fan[i]]+=j;
y-=j,k+=j;if(!y)break;
}
}
}
if(!k)d[x]=-1;
return k;
}
bool bfs(){
int i,head=0,tail=1,now;
fo(i,S,TT)d[i]=0;d[SS]=1;data[1]=SS;
while(head<tail){
now=data[++head];
rep(i,now){
if(!d[last[i]]&&chang[i]){
d[last[i]]=d[now]+1;
data[++tail]=last[i];
}
}
}
return d[TT];
}
int main(){
// freopen("fan.in","r",stdin);
// freopen("fan.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n)fo(j,1,m)scanf("%d",&x),hang[i]+=x,lie[j]+=x;
scanf("%d%d",&L,&R);
S=0,T=n+m+1;SS=T+1,TT=SS+1;
r=4000;
while(l<r){
mid=(l+r)/2;o=0;
yi=er=yy=ee=0;
fo(i,1,n){
if(max(L*m,hang[i]-mid)>min(R*m,hang[i]+mid)){yi=er+1;break;}
yi+=max(L*m,hang[i]-mid),er+=min(R*m,hang[i]+mid);
}
fo(i,1,m){
if(max(L*n,lie[i]-mid)>min(R*n,lie[i]+mid)){yy=ee+1;break;}
yy+=max(L*n,lie[i]-mid),ee+=min(R*n,lie[i]+mid);
}
if(er<yy||ee<yi||yi>er||yy>ee)l=mid+1;else r=mid;
}
printf("%d\n",l);
memset(first,0,sizeof(first));num=0;
fo(i,1,n)link(S,i,hang[i]+l,max(0,hang[i]-l));
fo(j,1,m)link(n+j,T,lie[j]+l,max(0,lie[j]-l));
fo(i,1,n)fo(j,1,m)link(i,j+n,R,L);
add(T,S,inf);
ans=0;while(bfs())ans+=dinic(SS,inf);
fo(i,1,n){
rep(j,i){
if(n+1<=last[j]&&last[j]<=n+m)
an[i][last[j]-n]=R-L-chang[j]+L;
}
}
fo(i,1,n){fo(j,1,m)printf("%d ",an[i][j]);printf("\n");}
}