2018.08.18【2018提高组】模拟A组题解(JZOJ5829)string

T1:

5831.string

input:string.in output:string.out
Time Limits: 1000 ms Memory Limits: 262144 KB Detailed Limits

题目描述

给定一个由小写字母组成的字符串 s。有 m 次操作,每次操作给 定 3 个参数 l,r,x。如果 x=1,将 s[l]~s[r]升序排序;如果 x=0,将 s[l]~s[r] 降序排序。你需要求出最终序列。

输入描述

第一行两个整数 n,m。第二行一个字符串 s。接下来 m 行每行三 个整数 l,r,x。

输出描述

一行一个字符串表示答案。

样例输入

5 2
cabcd
1 3 1
3 5 0

样例输出

abdcc

题解:

先说我(废)怎(柴)么(说)想(废)的(话)(直接下滑有分割线)
看到第一题就想到区间翻转,想到区间翻转就想起昨天看到的splay的区间插入与翻转,
然而我不会
想到区间就想到每个区间在被转后就有序了,能不能给区间打标记,合并区间时二分插入?
然而一点也不现实,
先不说怎么二分插入,合并多个区间时,我只想到一个一个向最大的区间插入
其时间复杂度还不如暴力快排
而且还要分裂合并区间,极其弱小的我只好发呆睡觉出去转
出去闲逛时,听到某su对某wu说了一句第一题splay。
于是还真以为splay正解,于是开始想······
__________________________**分割线**_________________________________
然而我还是不会,
但在想二分插入时我便想到了标记升序,逆序,与无序,这是一个思路
能不能用线段树合并区间?
如果我们给每个节点(代表一个区间)打上标记,再记录在这个区间中26个字母分别有多少个
不就可以知道其顺序了?
然后思路就清晰了。
我们可以先建出一棵线段树,维护以下每个节点所代表的区间中26个字母分别有多少个
然后就是区间排序了,
我们把l~r这个区间中26个字母分别有多少个算出来,
然后再按要求的顺序插入回线段树中,因为桶排起到了排序的效果,所以按顺序插入回去就行了
就这么简单?当然不止这些
如果我们每个节点都插入一次就会很自然的时超。
但我们可以打懒惰标记啊,由于我们知道一个区间中26个字母分别有多少个,又知道其顺序(升或降),输出和修改时按这个(随便搞搞)来操作不就行了?
可是如果区间互相覆盖呢?没关系,标记下传就行了。

修改区间的具体操作是这样的:
我们先统计l~r这个区间中26个字母分别有多少个,设gx[1~26]表示26个字母分别有多少个
二分查找l~r所包含的区间,
遇到有标记的点就用它存下的26个字母个数更新它的左右儿子(顺序按标记来),
然后把标记传给它的左右儿子(它的左右儿子还没更新时,其标记可能与真正顺序不同,但我们可以用其父亲的标记覆盖它),
这样就可以保证扫到不需查找的儿子时仍然有序
找到完全包含的区间就把其字母个数加入gx,否则二分查找。
统计完后分配到l~r中
如果完全包含一个区间则先将其清空,将gx剩下的字母中前(或后)r-l+1个字母塞进去,再打上有序的标记(0表示无序,1和2分别表示降序升序,这样下次扫到这里就可以更新其儿子)
否则我们继续二分查找

最后输出很简单,先左后右深搜,搜到一个有顺序标记的区间就按顺序输出
否则还是二分查找。

由于我代码太丑了,所以超了4000bytes。但时间还是(比较优秀)不太好。
大多C++选手不知跑得很慢,不过还是有一部分不开o3o2也很优秀而优美的,而有一些开了o2o3还是700+ms。
作为一个pascal选手500+ms我很骄傲。
这道题个人认为比较好改,毕竟是考场切的,有些地方不理解自己思考一下就好。
居然是新初二唯一一个切掉的,还是考场切掉的11个之一(113人),然而被两个新初二没切题拼分踩下去了,oj好卡,差二十分钟就交不上去了。第一次下夏令营A组18新初二第三
贴一段标

procedure zh(x,l,r,y,z:longint);//统计
var
    mid,vs,mp,o,i:longint;
begin
    if l<>r then
    begin
        mid:=(l+r)div 2;
        if (y<=l)and(z>=r) then for i:=1 to 26 do v[i]:=v[i]+t[x,i]//完全包含则全部加入
        else
        begin
            if bz[x]<>0 then
            begin
                if bz[x]=1 then vs:=26
                else vs:=1;//头指针
                bz[son[x,1]]:=bz[x];
                bz[son[x,2]]:=bz[x];
                t[son[x,1]]:=qk;
                t[son[x,2]]:=qk;
                cs:=t[x];
                mp:=mid-l+1;
                o:=1;
                while mp>0 do//下传
                begin
                    while cs[vs]=0 do
                    begin
                        if bz[x]=2 then inc(vs)
                        else dec(vs);
                    end;
                    if cs[vs]>=mp then
                    begin
                        cs[vs]:=cs[vs]-mp;
                        t[son[x,o],vs]:=mp;
                        mp:=0;
                        if o=1 then//塞完左区间塞右区间
                        begin
                            o:=2;
                            mp:=r-mid;
                        end
                        else break;
                    end
                    else
                    begin
                        mp:=mp-cs[vs];
                        t[son[x,o],vs]:=cs[vs];
                        cs[vs]:=0;
                    end;
                end;
                bz[x]:=0;
            end;
            if (y<=mid)and(z>=mid+1) then//二分查找
            begin
                zh(son[x,1],l,mid,y,z);
                zh(son[x,2],mid+1,r,y,z);
            end
            else
            begin
                if (z<=mid)then zh(son[x,1],l,mid,y,z)
                else zh(son[x,2],mid+1,r,y,z);
            end;
        end;
    end
    else for i:=1 to 26 do v[i]:=v[i]+t[x,i];
end;
procedure fp(x,l,r,y,z,bs:longint);//修改
var
    mid,mp,i:longint;
begin
    if l<>r then
    begin
        mid:=(l+r)div 2;
        if (y<=l)and(z>=r) then
        begin
            bz[x]:=bs;
            t[x]:=qk;
            mp:=r-l+1;
            while mp<>0 do//塞满整个区间
            begin
                while v[kt]=0 do//更新头指针,初始值按升序降序来定
                begin
                    if bs=2 then inc(kt)
                    else dec(kt);
                end;
                if v[kt]>mp then
                begin
                    v[kt]:=v[kt]-mp;
                    t[x,kt]:=mp;
                    mp:=0;
                end
                else
                begin
                    mp:=mp-v[kt];
                    t[x,kt]:=v[kt];
                    v[kt]:=0;
                end;
            end;
        end
        else
        begin//二分查找
            if (y<=mid)and(z>=mid+1) then
            begin
                fp(son[x,1],l,mid,y,z,bs);
                fp(son[x,2],mid+1,r,y,z,bs);
            end
            else
            begin
                if (z<=mid)then fp(son[x,1],l,mid,y,z,bs)
                else fp(son[x,2],mid+1,r,y,z,bs);
            end;
            for i:=1 to 26 do t[x,i]:=t[son[x,1],i]+t[son[x,2],i];
        end;
    end
    else
    begin
        t[x]:=qk;
        while v[kt]=0 do
        begin
            if bs=2 then inc(kt)
            else dec(kt);
        end;
        v[kt]:=v[kt]-1;
        t[x,kt]:=1;
    end;
end;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值