模擬費用流,從左往右依次考慮每個訂單,在下面兩種情況里選代價較小的進行增廣。
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; }