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