CXXIV.[GYM102155J]stairways
首先,考虑暴力 \(n^3\) DP——设 \(f_{i,j,k}\) 表示当前DP到第 \(i\) 个人,且第一条楼梯上到的最晚的人在时刻 \(j\) 到达,第二条楼梯在时刻 \(k\)。
然后,观察到 \(j,k\) 中至少有一个值为前缀 \(\max\) 的时刻值。故状态可以被压缩到二维——\(f_{i,j}\) 表示第一条楼梯上的人是前缀 \(\max\),第二条楼梯在时刻 \(k\)。
设前缀 \(max\) 为 \(s\)。然后有
考虑 \(f_{i,j}\),其中肯定应该是 \(j\) 越小,值越大,即 \(f_{i,j}\) 应是单调递减的。假如在某个地方单调性被破坏,那肯定是在后方的东西更劣,可以直接舍去。
于是我们考虑全程只使用一个 \(f\) 数组,随着 \(i\) 增加改变里面的东西。设 \(f'\) 表示老的 \(f\) 数组,然后,假设我们已经保证了 \(f\) 的单调性,则转移式可以直接变为:
然后,\(a_i\) 可以在最后统一减去,优化得
于是我们现在要解决两个问题,一是区间加定值 \(s\),这个简单用个什么线段树平衡树之类轻松解决;关键是第二个问题,后缀全部增加 \(j\),同时还要维护单调性。
我们对于每个位置 \(j\),设小于它的最大元素是 \(k\),维护一个值为 \(\left\lfloor\dfrac{f_k-f_j}{j-k}\right\rfloor\)。当后缀带权加时,明显这个值会减少 \(1\)。而当这个值最终减少到 \(0\) 时,就说明单调性被破坏,可以删掉该元素了。
在前缀加 \(s\) 时,上述值并没有被改变;在修改 \(f_{a_i}\) 的值时,只有 \(a_i\) 附近的东西被改变了,暴力修改即可;在后缀带权加时,除了起始的地方,其他地方的值是全部减一的,于是仍然暴力修改起始处的值即可。
可以使用线段树,但这样就没法很方便地维护单调性;无论是分块还是平衡树维护都可以起到简单维护单调性的功效,直接上即可。
时间复杂度 \(O(n\sqrt{n})\) 或 \(n\log n\),取决于使用哪种数据结构。这里采取平衡树。
代码:
/*
f[i]=f[j] (j is the maximal element smaller than i
f[j]=f[j]+s-i (j<i)
f[j]=f[j]+j-i (j>i)
tms=upper[(f[k]-f[j])/(j-k)] where k<j.
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,a[100100],rt,cnt;
#define lson t[x].ch[0]
#define rson t[x].ch[1]
struct Treap{
int key,rd,ch[2],sz;
ll f,tagf,tagt,tms,mn;
}t[100100];
void pushup(int x){
t[x].mn=t[x].tms,t[x].sz=1;
if(lson)t[x].mn=min(t[x].mn,t[lson].mn),t[x].sz+=t[lson].sz;
if(rson)t[x].mn=min(t[x].mn,t[rson].mn),t[x].sz+=t[rson].sz;
}
void ADDF(int x,ll y){if(x)t[x].tagf+=y,t[x].f+=y;}
void ADDT(int x,ll y=1){if(x)t[x].tagt+=y,t[x].tms-=y,t[x].mn-=y,t[x].f+=1ll*y*t[x].key;}
void pushdown(int x){
ADDF(lson,t[x].tagf),ADDT(lson,t[x].tagt);
ADDF(rson,t[x].tagf),ADDT(rson,t[x].tagt);
t[x].tagf=t[x].tagt=0;
}
int merge(int x,int y){
if(!x||!y)return x+y;
if(t[x].rd>t[y].rd){pushdown(x),t[x].ch[1]=merge(t[x].ch[1],y),pushup(x);return x;}
else{pushdown(y),t[y].ch[0]=merge(x,t[y].ch[0]),pushup(y);return y;}
}
void splitbykey(int x,int k,int &u,int &v){//u:<k.
if(!x){u=v=0;return;}
pushdown(x);
if(t[x].key<k)u=x,splitbykey(rson,k,rson,v);
else v=x,splitbykey(lson,k,u,lson);
pushup(x);
}
void splitbysize(int x,int k,int &u,int &v){
if(!x){u=v=0;return;}
pushdown(x);
if(t[lson].sz>=k)v=x,splitbysize(lson,k,u,lson);
else u=x,splitbysize(rson,k-t[lson].sz-1,rson,v);
pushup(x);
}
int Newnode(int key,ll f){
int x=++cnt;
t[x].f=f,t[x].key=key,t[x].rd=rand()*rand();
pushup(x);
return x;
}
ll flor(ll x,ll y){//the floor-rounded of x/y.
if(x<=0)return 0;
return (x-1)/y+1;
}
void func(int k,int pre){
int a,b,c,d,e;
splitbykey(rt,k,b,c);
ADDF(b,pre);
splitbysize(b,t[b].sz-1,a,b);
splitbykey(c,k+1,c,d);
if(!c)c=Newnode(k,0),t[c].f=t[b].f+k-pre;//because f[b] has already been added by pre.
else t[c].f+=k;
t[c].tms=flor(t[b].f-t[c].f,t[c].key-t[b].key);
pushup(c);
ADDT(d);
splitbysize(d,1,d,e);
if(d)t[d].tms=flor(t[c].f-t[d].f,t[d].key-t[c].key),pushup(d);
rt=merge(merge(merge(merge(a,b),c),d),e);
}
int getmn(int x){
pushdown(x);
if(lson&&t[lson].mn<=0)return getmn(lson);
if(t[x].tms<=0)return t[x].key;
return getmn(rson);
}
void reset(){
while(rt&&t[rt].mn<=0){
int k=getmn(rt);
int a,b,c,d,e;
splitbykey(rt,k,b,c);
splitbysize(b,t[b].sz-1,a,b);
splitbykey(c,k+1,c,d);
splitbysize(d,1,d,e);
if(d)t[d].tms=flor(t[b].f-t[d].f,t[d].key-t[b].key),pushup(d);
rt=merge(merge(a,b),merge(d,e));
}
}
ll getres(){
int a,b;
splitbysize(rt,t[rt].sz-1,a,b);
ll tmp=t[b].f;
rt=merge(a,b);
return tmp;
}
void iterate(int x){
if(!x)return;
pushdown(x);
iterate(lson);
printf("%d:(F:%d TMS:%d MN:%d)\n",t[x].key,t[x].f,t[x].tms,t[x].mn);
iterate(rson);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
rt=Newnode(0,a[1]),t[rt].tms=0x3f3f3f3f;
for(int i=2,j=a[1];i<=n;i++){
j=max(j,a[i]);
func(a[i],j);
// puts("BEF:");iterate(rt);
reset();
// puts("AFT:");iterate(rt);puts("");
}
ll res=getres();
for(int i=1;i<=n;i++)res-=a[i];
printf("%lld\n",res);
return 0;
}
/*
9
44 43 42 41 33 66 32 11 5
*/