zoukankan      html  css  js  c++  java
  • [GYM102155J]stairways

    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}=\begin{cases}f_{i-1,j}+s-a_i&(j<a_i)\\\min\limits_{j\leq a_i}f_{i-1,j}&(j=a_i)\\f_{i-1,j}+j-a_i&(j>a_i)\end{cases} \]

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

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

    \[f_{j}=\begin{cases}f_{j}'+s-a_i&(j<a_i)\\f_{k}'&(j=a_i,\text{k是小于等于j的最大元素})\\f_{j}'+j-a_i&(j>a_i)\end{cases} \]

    然后,\(a_i\) 可以在最后统一减去,优化得

    \[f_{j}=\begin{cases}f_{j}'+s&(j<a_i)\\f_{k}'+a_i&(j=a_i,\text{k是小于等于j的最大元素})\\f_{j}'+j&(j>a_i)\end{cases} \]

    于是我们现在要解决两个问题,一是区间加定值 \(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
    */
    

  • 相关阅读:
    Ubuntu 侧边栏和顶栏设置
    ubuntu 下安装微软字体和 console
    vim 的 auto-pairs 设置
    linux上的常用的一些操作
    断点模式
    GIT(git)简单操作
    制表符 的用法
    如何解决ASCII 字符显示不出来的情况
    01_js 快速入门
    神代码,结束进程神方法
  • 原文地址:https://www.cnblogs.com/Troverld/p/14601437.html
Copyright © 2011-2022 走看看