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; }