模拟费用流,从左往右依次考虑每个订单,在下面两种情况里选代价较小的进行增广。
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; }