T1:
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;