[SPLAY维护区间][BZOJ 3223][TYVJ 1729]文艺平衡树

Description

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1

Analysis

那时TYVJ还没倒闭的时候,我平衡树学的是SBT...虽然常数完爆splay,但是maintain太难记了囧(现在已经全忘了我会说么)。

那时看到这道题没思路,之后noip后tyvj就倒闭了,也就没有接着做这题了。

然后某天晚上无聊切了[NOI2005]维修数列(其实还有一个下午),发现splay还能干这种事,而且好记又好写,卡常数这种丧心病狂的事又不是很可能遇到,就果断放弃SBT了...

题目相对于维修数列算是极水的,对于维护区间问题不会的,可以去翻JZP的论文和代码(C++和PASCAL都有)

上渣码:

type rec=record fa,l,r,val,siz:longint; rev:boolean; end;
var
  n,m,i,x,y,root,tmp_root,total:longint;
  t:array[-1..100010] of rec;
function new(x:longint):longint;
begin
  inc(total);
  new:=total;
  with t[new] do begin
    fa:=-1; l:=-1; r:=-1; val:=x; rev:=false; siz:=1;
  end;
end;
procedure reverse(x:longint);
var
  tt:longint;
begin
  if x=-1 then exit;
  tt:=t[x].l; t[x].l:=t[x].r; t[x].r:=tt;
  t[x].rev:=not t[x].rev;
end;
procedure updata(x:longint);
begin
  if x=-1 then exit;
  t[x].siz:=t[t[x].l].siz+t[t[x].r].siz+1;
end;
procedure push_down(x:longint);
begin
  if x=-1 then exit;
  if t[x].rev then begin
    reverse(t[x].l);
    reverse(t[x].r);
    t[x].rev:=false;
  end;
end;
procedure zig(x:longint);
var
  y,tmp:longint;
begin
  y:=t[x].fa; tmp:=t[x].r;
  push_down(y);
  push_down(x);
  if t[t[y].fa].l=y then t[t[y].fa].l:=x else t[t[y].fa].r:=x;
  t[x].fa:=t[y].fa; t[x].r:=y;
  t[y].l:=tmp; t[y].fa:=x; t[tmp].fa:=y;
  updata(y);
end;
procedure zag(x:longint);
var
  y,tmp:longint;
begin
  y:=t[x].fa; tmp:=t[x].l;
  push_down(y);
  push_down(x);
  if t[t[y].fa].l=y then t[t[y].fa].l:=x else t[t[y].fa].r:=x;
  t[x].fa:=t[y].fa; t[x].l:=y;
  t[y].r:=tmp; t[y].fa:=x; t[tmp].fa:=y;
  updata(y);
end;
procedure splay(x,aim:longint);
var
  y,tmp:longint;
begin
  if x=-1 then exit;
  push_down(x);
  tmp:=t[aim].fa;
  while t[x].fa<>tmp do begin
    y:=t[x].fa;
    if t[y].fa=tmp then if t[y].l=x then zig(x) else zag(x)
    else begin
      if t[t[y].fa].l=y then begin if t[y].l=x then zig(y) else zag(x); zig(x); end
      else begin if t[y].r=x then zag(y) else zig(x); zag(x); end;
    end;
  end;
  updata(x);
  if tmp=-1 then root:=x;
end;
procedure select(k,aim:longint);
var
  tt:longint;
begin
  tt:=root;
  while t[t[tt].l].siz+1<>k do begin
    push_down(tt);
    if t[t[tt].l].siz>=k then tt:=t[tt].l
    else begin k:=k-t[t[tt].l].siz-1; tt:=t[tt].r; end;
  end;
  splay(tt,aim);
end;
function make_tree(l,r:longint):longint;
var
  m,x:longint;
begin
  if l>r then exit(-1);
  m:=(l+r) shr 1;
  x:=new(m);
  t[x].l:=make_tree(l,m-1);
  t[x].r:=make_tree(m+1,r);
  if t[x].l<>-1 then t[t[x].l].fa:=x;
  if t[x].r<>-1 then t[t[x].r].fa:=x;
  updata(x);
  exit(x);
end;
begin
  root:=new(0);
  t[root].r:=new(0);
  t[t[root].r].fa:=root;
  updata(root);
  readln(n,m);
  tmp_root:=make_tree(1,n);
  t[t[root].r].l:=tmp_root;
  t[tmp_root].fa:=t[root].r;
  splay(t[root].r,root);
  for i:=1 to m do begin
    readln(x,y);
    select(x,root);
    select(y+2,t[root].r);
    reverse(t[t[root].r].l);
  end;
  for i:=1 to n do begin
    select(i+1,root);
    write(t[root].val,' ');
  end;
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值