BZOJ1920 : [Ctsc2010]产品销售

模拟费用流,从左往右依次考虑每个订单,在下面两种情况里选代价较小的进行增广。

 

1. 产品在订单之后产生:

因为之前考虑的订单都在当前订单的左侧,因此往右走时不会遇到反悔边。

线段树上查询出对应后缀内仍能供给的且代价最小的生产季度,然后将该区间内的往左走的边的流量增加$1$,表示反悔边。

 

2. 产品在订单之前产生:

如果一条往左走的边有流量,那么代价为$-c$,否则代价为$m$。

在线段树上找到到当前订单总代价最小的仍能供给的生产季度,然后将该区间内的左走的边的流量减少$1$。

 

注意到一条边的流量只会经历$0\rightarrow$正数$\rightarrow 0$的过程,因此在线段树上维护每个区间内流量的最小值、正流量的最小值,然后在区间修改时通过最值判断该区间内是否有边从$0$变成正数或者从正数变成$0$,如果没有的话直接打标记,否则暴力递归。

时间复杂度$O(n\log n)$。

 

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100010,M=262150;
const ll inf=1LL<<50;
int n,i,d[N],u[N],p[N],cl[N],cr[N],s[N];
ll ans,tag[M],mi[M],mip[M];int vr[M];
struct E{
  ll sum,best;int pos;
  void operator+=(const E&b){
    sum+=b.sum;
    if(best+b.sum<b.best){
      if(best<inf)best+=b.sum;
    }else best=b.best,pos=b.pos;
  }
}vl[M],tmp;
bool flag;
inline int merge(int a,int b){
  if(!a||!b)return a+b;
  return s[a]+p[a]<s[b]+p[b]?a:b;
}
inline void set(E&e,int x,int o){
  e.sum=o?-cr[x]:cl[x];
  if(u[x])e.best=e.sum+p[x],e.pos=x;else e.best=inf,e.pos=0;
}
void build(int x,int a,int b){
  mip[x]=inf;
  if(a==b){
    set(vl[x],a,0);
    vr[x]=u[a]?a:0;
    return;
  }
  int mid=(a+b)>>1;
  build(x<<1,a,mid),build(x<<1|1,mid+1,b);
  vl[x]=vl[x<<1];
  vl[x]+=vl[x<<1|1];
  vr[x]=merge(vr[x<<1],vr[x<<1|1]);
}
inline void tag1(int x,ll p){
  tag[x]+=p;mi[x]+=p;
  if(mi[x]<0)mi[x]=0;
  if(mip[x]<inf)mip[x]+=p;
}
inline void pb(int x){if(tag[x])tag1(x<<1,tag[x]),tag1(x<<1|1,tag[x]),tag[x]=0;}
void plus(int x,int a,int b,int c,int d,int p){
  if(c<=a&&b<=d){
    if(mi[x]){tag1(x,p);return;}
    if(a==b){
      mi[x]+=p;
      mip[x]=mi[x]?mi[x]:inf;
      set(vl[x],a,mi[x]);
      return;
    }
  }
  pb(x);
  int mid=(a+b)>>1;
  if(c<=mid)plus(x<<1,a,mid,c,d,p);
  if(d>mid)plus(x<<1|1,mid+1,b,c,d,p);
  vl[x]=vl[x<<1];
  vl[x]+=vl[x<<1|1];
  mi[x]=min(mi[x<<1],mi[x<<1|1]);
  mip[x]=min(mip[x<<1],mip[x<<1|1]);
}
void minus(int x,int a,int b,int c,int d,int p){
  if(c<=a&&b<=d){
    if(mip[x]>p){tag1(x,-p);return;}
    if(a==b){
      mi[x]=max(mi[x]-p,0LL);
      mip[x]=mi[x]?mi[x]:inf;
      set(vl[x],a,mi[x]);
      return;
    }
  }
  pb(x);
  int mid=(a+b)>>1;
  if(c<=mid)minus(x<<1,a,mid,c,d,p);
  if(d>mid)minus(x<<1|1,mid+1,b,c,d,p);
  vl[x]=vl[x<<1];
  vl[x]+=vl[x<<1|1];
  mi[x]=min(mi[x<<1],mi[x<<1|1]);
  mip[x]=min(mip[x<<1],mip[x<<1|1]);
}
void change(int x,int a,int b,int c){
  if(a==b){
    set(vl[x],a,mi[x]);
    vr[x]=u[a]?a:0;
    return;
  }
  pb(x);
  int mid=(a+b)>>1;
  if(c<=mid)change(x<<1,a,mid,c);else change(x<<1|1,mid+1,b,c);
  vl[x]=vl[x<<1];
  vl[x]+=vl[x<<1|1];
  vr[x]=merge(vr[x<<1],vr[x<<1|1]);
}
int askr(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d)return vr[x];
  int mid=(a+b)>>1,t=0;
  if(c<=mid)t=askr(x<<1,a,mid,c,d);
  if(d>mid)t=merge(t,askr(x<<1|1,mid+1,b,c,d));
  return t;
}
void askl(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d){
    if(!flag)flag=1,tmp=vl[x];
    else tmp+=vl[x];
    return;
  }
  pb(x);
  int mid=(a+b)>>1;
  if(c<=mid)askl(x<<1,a,mid,c,d);
  if(d>mid)askl(x<<1|1,mid+1,b,c,d);
}
ll askmip(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d)return mip[x];
  pb(x);
  int mid=(a+b)>>1;ll t=inf;
  if(c<=mid)t=askmip(x<<1,a,mid,c,d);
  if(d>mid)t=min(t,askmip(x<<1|1,mid+1,b,c,d));
  return t;
}
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
int main(){
  read(n);
  for(i=1;i<=n;i++)read(d[i]);
  for(i=1;i<=n;i++)read(u[i]);
  for(i=1;i<=n;i++)read(p[i]);
  for(i=1;i<n;i++)read(cl[i]);
  for(i=1;i<n;i++)read(cr[i]);
  for(i=1;i<=n;i++)s[i]=s[i-1]+cr[i-1];
  build(1,1,n);
  for(i=1;i<=n;i++)while(d[i]){
    int pr=askr(1,1,n,i,n),pl=0;
    ll costr=inf,costl=inf,flow;
    if(pr)costr=s[pr]-s[i]+p[pr];
    if(i>1){
      flag=0;
      askl(1,1,n,1,i-1);
      if(tmp.pos)pl=tmp.pos,costl=tmp.best;
    }
    if(costl<costr){
      flow=min(1LL*min(d[i],u[pl]),askmip(1,1,n,pl,i-1));
      d[i]-=flow;
      u[pl]-=flow;
      ans+=flow*costl;
      change(1,1,n,pl);
      minus(1,1,n,pl,i-1,flow);
    }else{
      flow=min(d[i],u[pr]);
      d[i]-=flow;
      u[pr]-=flow;
      ans+=flow*costr;
      change(1,1,n,pr);
      if(i<pr)plus(1,1,n,i,pr-1,flow);
    }
  }
  return printf("%lld",ans),0;
}

  

posted @ 2020-01-22 02:55  Claris  阅读(834)  评论(1编辑  收藏  举报