zoukankan      html  css  js  c++  java
  • BZOJ1920 : [Ctsc2010]产品销售

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

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

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

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

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

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

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

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

    时间复杂度$O(nlog 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;
    }
    

      

  • 相关阅读:
    笔记1
    笔记2
    笔记3
    两个多线程的交替打印
    三个多线程的交替打印
    内部类
    基本反射了解
    键盘监听事件KeyListener
    焦点监听事件FocusListener
    文本框JTextField,密码框JPasswordField
  • 原文地址:https://www.cnblogs.com/clrs97/p/12227751.html
Copyright © 2011-2022 走看看