[GYM102155J]stairways

CXXIV.[GYM102155J]stairways

首先,考虑暴力 n3 DP——设 fi,j,k 表示当前DP到第 i 个人,且第一条楼梯上到的最晚的人在时刻 j 到达,第二条楼梯在时刻 k

然后,观察到 j,k 中至少有一个值为前缀 max 的时刻值。故状态可以被压缩到二维——fi,j 表示第一条楼梯上的人是前缀 max,第二条楼梯在时刻 k

设前缀 maxs。然后有

fi,j={fi1,j+sai(j<ai)minjaifi1,j(j=ai)fi1,j+jai(j>ai)

考虑 fi,j,其中肯定应该是 j 越小,值越大,即 fi,j 应是单调递减的。假如在某个地方单调性被破坏,那肯定是在后方的东西更劣,可以直接舍去。

于是我们考虑全程只使用一个 f 数组,随着 i 增加改变里面的东西。设 f 表示老的 f 数组,然后,假设我们已经保证了 f 的单调性,则转移式可以直接变为:

fj={fj+sai(j<ai)fk(j=ai,k是小于等于j的最大元素)fj+jai(j>ai)

然后,ai 可以在最后统一减去,优化得

fj={fj+s(j<ai)fk+ai(j=ai,k是小于等于j的最大元素)fj+j(j>ai)

于是我们现在要解决两个问题,一是区间加定值 s,这个简单用个什么线段树平衡树之类轻松解决;关键是第二个问题,后缀全部增加 j,同时还要维护单调性。

我们对于每个位置 j,设小于它的最大元素是 k,维护一个值为 fkfjjk。当后缀带权加时,明显这个值会减少 1。而当这个值最终减少到 0 时,就说明单调性被破坏,可以删掉该元素了。

在前缀加 s 时,上述值并没有被改变;在修改 fai 的值时,只有 ai 附近的东西被改变了,暴力修改即可;在后缀带权加时,除了起始的地方,其他地方的值是全部减一的,于是仍然暴力修改起始处的值即可。

可以使用线段树,但这样就没法很方便地维护单调性;无论是分块还是平衡树维护都可以起到简单维护单调性的功效,直接上即可。

时间复杂度 O(nn)nlogn,取决于使用哪种数据结构。这里采取平衡树。

代码:

/*
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
*/

posted @   Troverld  阅读(86)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示