BZOJ1889 : Maximal

二分答案,判断是否存在合法方案使得每个数都不超过$mid$。

考虑网络流建图:

  • $i$点的流量下限为$\max(a_i-mid,0)$,费用为$1$,故拆点进行限制。
  • $i$向$i+1$、$S$向$i$、$i$向$T$连边,费用为$0$。

那么一条增广路径对应选择一个区间进行减$1$。

求出流量不超过$K$时的最小费用可行流,若有解且费用不超过$M$,则可行。

 

#include<cstdio>
const int N=510,M=100010,inf=~0U>>2;
int n,K,m,i,a[N],L,R,mx,MID,ans,flow,cost,tmp;
int u[M],v[M],c[M],co[M],nxt[M],t,S,T,SS,TT,l,r,q[M],g[N],lim[N],f[N],d[N];bool in[N];
inline void add(int x,int y,int l,int r,int zo){
  lim[x]-=l,lim[y]+=l;cost+=l*zo;
  r-=l;
  if(!r)return;
  u[++t]=x;v[t]=y;c[t]=r;co[t]=zo;nxt[t]=g[x];g[x]=t;
  u[++t]=y;v[t]=x;c[t]=0;co[t]=-zo;nxt[t]=g[y];g[y]=t;
}
bool spfa(){
  int x,i;
  for(i=1;i<=TT;i++)d[i]=inf,in[i]=0;
  d[SS]=0;in[SS]=1;l=r=M>>1;q[l]=SS;
  while(l<=r){
    x=q[l++];
    if(x==TT)continue;
    for(i=g[x];i;i=nxt[i])if(c[i]&&co[i]+d[x]<d[v[i]]){
      d[v[i]]=co[i]+d[x];f[v[i]]=i;
      if(!in[v[i]]){
        in[v[i]]=1;
        if(d[v[i]]<d[q[l]])q[--l]=v[i];else q[++r]=v[i];
      }
    }
    in[x]=0;
  }
  return d[TT]<inf;
}
bool check(){
  flow=cost=0;
  for(t=i=1;i<=TT;i++)g[i]=lim[i]=0;
  for(i=1;i<=n;i++){
    add(S,i,0,K,0);
    add(i+n,T,0,K,0);
    if(i<n)add(i+n,i+1,0,K,0);
    add(i,i+n,a[i]>MID?a[i]-MID:0,mx,1);
  }
  add(T,S,0,K,0);
  for(i=1;i<=T;i++)if(lim[i]>0)add(SS,i,0,lim[i],0),flow+=lim[i];else add(i,TT,0,-lim[i],0);
  while(spfa()){
    for(tmp=inf,i=TT;i!=SS;i=u[f[i]])if(tmp>c[f[i]])tmp=c[f[i]];
    for(flow-=tmp,cost+=d[i=TT]*tmp;i!=SS;i=u[f[i]])c[f[i]]-=tmp,c[f[i]^1]+=tmp;
  }
  return !flow&&cost<=m;
}
int main(){
  scanf("%d%d%d",&n,&K,&m);
  S=n*2+1;T=S+1;SS=T+1;TT=SS+1;
  for(i=1;i<=n;i++){
    scanf("%d",&a[i]);
    if(R<a[i])R=a[i];
  }
  mx=ans=R--;
  while(L<=R){
    MID=(L+R)>>1;
    if(check())R=(ans=MID)-1;else L=MID+1;
  }
  return printf("%d",ans),0;
}

  

posted @ 2017-07-08 05:08  Claris  阅读(172)  评论(0编辑  收藏  举报