GalaxyOJ-727 (线段树区间最小值)

题目

Problem Description

wzj同学熟悉电脑操作,于是他找了一个打字员的工作。他的工作主要是编辑一个有n行的文本,每一行的字符数量不尽相同。如我们所知的一样,文本中有一个光标,它一开始在第1行的最后一个字符后面,wzj可以在光标的位置输入或删除字符。当然,他也可以用方向键移动光标,可以往上、下、左、右移动。要注意的是,如果当前光标所在的位置前面有a个字符,如果要将其向下(上)移动,而下(上)一行的字符不足a个,那么光标就会落在下(上)一行的最后一个字符后面。

现在,wzj知道这个文本的行数n和每一行所含的字符数ai,他的上司给了他一个操作序列,共有q个操作,操作分为3种,如下:

To a:将光标移到第a行的最后一个字符之后。

Insert a:在当前光标所在行的末端插入a个字符。

Delete a:在当前光标所在行的末端删除a个字符。

已知向上、下、左、右移动一格,插入或删除一个字符都需要1次按键,而wzj又是一个很懒的人,他想知道要完成这些操作至少需要多少次按键。特殊地,如果删除操作中当前行的字符不足a个,则按键数应为该行有的字符数。

Input

第一行有2个以空格间隔的整数n和q,意义如题目描述中所述。

接下来的一行,有n个以空格间隔的整数,其中第i个整数表示第i行的字符数ai。

接下来的q行,每一行有一个操作指令,格式如题目描述中所述。

1≤n,q≤200000,操作过程中每行的字符数不超过1000000,输入的指令均合法,行首没有空格。

Output

输出一个整数,为完成操作至少所需的按键次数(结果可能较大,建议使用long long型存储)。

Sample Input

5 5
2 3 2 4 2
To 2
Insert 2
To 4
Delete 5
To 2

Sample Output

19

样例说明(移动指令中上下左右以UDLR代替)

第1个操作,以“DR”顺序移动,按键数为2。
第2个操作,按键数为2,第2行字符数变为5。
第3个操作,以“DDRR”顺序移动,按键数为4。
第4个操作,第4行字符数不足5个,所以只需4次按键,第4行字符数变为0。
第5个操作,以“URRURRR”顺序移动,按键数为7。
所以按键数总共为2+2+4+4+7=19次。

分析

  • 我们发现,每次操作后光标都在当前行最后面,而从第 i 行移到第 j 行,只按上下到那一行后,光标的位置是区间 [i,j] 字符数的最小值,之后再向右移到最后面就行了。
  • 鉴于数据范围,就用线段树维护一下每行字符数的区间最小值。

程序

#include <cstdio>
#include <algorithm>
#include <cstring>
#define Mid ((l+r)>>1)
#define L (x<<1)
#define R (L+1)
using namespace std;
int t[800005],a[200005],aim,Min,Max,nx,ny,n,q,k;
char ch[20];
long long ans;

void bui(int x,int l,int r){
    if (l==r){a[l]=(scanf("%d",&t[x]),t[x]); return;}
    bui(L,l,Mid);
    bui(R,Mid+1,r);
    t[x]=min(t[L],t[R]);
}

int que(int x,int l,int r){
    if (l>=Min && r<=Max) return t[x];
    int xx=2147483647,yy=2147483647;
    if (Mid>=Min) xx=que(L,l,Mid);
    if (Mid+1<=Max) yy=que(R,Mid+1,r);
    return min(xx,yy);
}

void chg(int x,int l,int r){
    if (l==r){t[x]+=aim; return;}
    if (Mid>=nx) chg(L,l,Mid);
    if (Mid+1<=nx) chg(R,Mid+1,r);
    t[x]=min(t[L],t[R]);
}

int main(){
    memset(t,0x7f,sizeof(t));
    scanf("%d%d",&n,&q);
    bui(1,1,n);
    nx=1,ny=a[1];
    while (q--){
        scanf("%s %d",ch,&aim);
        if (ch[0]=='T'){
            ans+=abs(aim-nx);
            Min=min(nx,aim);
            Max=max(nx,aim);
            k=que(1,1,n); ans+=abs(a[aim]-k);
            nx=aim,ny=a[aim];
        }   
        if (ch[0]=='I'){
            chg(1,1,n);
            a[nx]=ny+=aim;
            ans+=aim;
        }
        if (ch[0]=='D'){
            aim=-min(ny,aim);
            chg(1,1,n);
            a[nx]=ny+=aim;
            ans-=aim;
        }
    }
    printf("%lld",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值