[BZOJ2653]middle 主席树+二分答案

中位数的题目一定要想到二分答案然后把所有数变成1或-1什么的。。。
先离散化,这样最多只有n个值,然后对于每个值建主席树,>=的设为1,<的设为-1。按照升序建的话每次最多更改一个值,也就是logN个点。
设询问区间[ a, b] [ c, d]为然后二分答案K,现在要求的就是在主席树的第K个版本中(b,c)的和,[ a, b]的最大右子段和,[ c, d]的最大左子段和,三个东西加起来是否大于0。所以线段树中维护sum,maxl,maxr。
maxl,maxr的求法和sum差不多,yy一下。。。
代码:

type
  node=record
    t,id:longint;
  end;
  tree=^treenode;
  treenode=record
    l,r,maxl,maxr,sum:longint;
    ls,rs:tree;
  end;

var
  n,m,i,j,last,size:longint;
  a:array[0..20010]of node;
  b,vsn:array[0..20010]of longint;
  tr:array[0..20010]of tree;
  q:array[1..4]of longint;
function max(x,y:longint):longint;
begin
  if x>y then exit(x)
  else exit(y);
end;
procedure qs(p,q:longint);
var
  i,j:longint;
  t,mid:node;
begin
  i:=p;
  j:=q;
  mid:=a[(p+q)div 2];
  repeat
    while a[i].t<mid.t do inc(i);
    while a[j].t>mid.t do dec(j);
    if i<=j then
    begin
      t:=a[i];
      a[i]:=a[j];
      a[j]:=t;
      inc(i);
      dec(j);
    end;
  until i>j;
  if p<j then qs(p,j);
  if i<q then qs(i,q);
end;
procedure px;
var
  i,j,t:dword;
begin
  for i:=1 to 4 do
    for j:=i+1 to 4 do
      if q[i]>q[j]  then begin t:=q[i]; q[i]:=q[j]; q[j]:=t; end;
end;
procedure update(x:tree);
begin
  x^.sum:=x^.ls^.sum+x^.rs^.sum;
  x^.maxl:=max(x^.ls^.maxl,x^.ls^.sum+x^.rs^.maxl);
  x^.maxr:=max(x^.rs^.maxr,x^.rs^.sum+x^.ls^.maxr);
end;
procedure newx(x:tree;l,t:longint);
begin
  x^.l:=l;
  x^.r:=l;
  x^.sum:=t;
  x^.maxl:=t;
  x^.maxr:=t;
  x^.ls:=nil;
  x^.rs:=nil;
end;
procedure build(x:tree;l,r:longint);
var
  mid:longint;
begin
  if l=r then
  begin
    newx(x,l,1);
    exit;
  end;
  x^.l:=l;
  x^.r:=r;
  mid:=(l+r)div 2;
  new(x^.ls);
  build(x^.ls,l,mid);
  new(x^.rs);
  build(x^.rs,mid+1,r);
  update(x);
end;

procedure rebuild(x,y:tree;v:longint);
var
  mid:longint;
begin
  if x^.l=x^.r then
  begin
    newx(y,x^.l,-1);
    exit;
  end;
  y^:=x^;
  mid:=(x^.l+x^.r)div 2;
  if v<=mid then
  begin
    new(y^.ls);
    rebuild(x^.ls,y^.ls,v);
  end
  else
  begin
    new(y^.rs);
    rebuild(x^.rs,y^.rs,v);
  end;
  update(y);
end;
function getsum(x:tree;l,r:longint):longint;
var
  mid:longint;
begin
  if l>r then exit(0);
  if (x^.l=l)and(x^.r=r) then exit(x^.sum);
  mid:=(x^.l+x^.r)div 2;
  if r<=mid then exit(getsum(x^.ls,l,r));
  if l>mid then exit(getsum(x^.rs,l,r));
  exit(getsum(x^.ls,l,mid)+getsum(x^.rs,mid+1,r));
end;
function getmaxl(x:tree;l,r:longint):longint;
var
  mid:longint;
begin
  if (x^.l=l)and(x^.r=r) then begin exit(x^.maxl);  end;
  mid:=(x^.l+x^.r)div 2;
  if r<=mid then exit(getmaxl(x^.ls,l,r));
  if l>mid then exit(getmaxl(x^.rs,l,r));
  if (r>mid)and (l<=mid) then exit(max(getmaxl(x^.ls,l,mid),getsum(x^.ls,l,mid)+getmaxl(x^.rs,mid+1,r)));
end;
function getmaxr(x:tree;l,r:longint):longint;
var
  mid:longint;
begin
  if (x^.l=l)and(x^.r=r) then exit(x^.maxr);
  mid:=(x^.l+x^.r)div 2;
  if r<=mid then exit(getmaxr(x^.ls,l,r));
  if l>mid then exit(getmaxr(x^.rs,l,r));
  exit(max(getmaxr(x^.rs,mid+1,r),getsum(x^.rs,mid+1,r)+getmaxr(x^.ls,l,mid)));
end;
function cal(r:longint):longint;
begin
  cal:=getsum(tr[vsn[r]],q[2]+1,q[3]-1)+getmaxl(tr[vsn[r]],q[3],q[4])+getmaxr(tr[vsn[r]],q[1],q[2]);
end;
function solve():longint;
var
  le,ri,mid:longint;
begin
  le:=1;
  ri:=size+1;
  while le<ri do
  begin
    mid:=(le+ri)div 2;
    if cal(mid)<0 then ri:=mid
    else le:=mid+1;
  end;
  exit(le-1);
end;
begin
  readln(n);
  for i:=1 to n do
  begin
    read(a[i].t);
    a[i].id:=i;
  end;
  qs(1,n);

  a[0].t:=0;
  last:=a[1].t;
  b[1]:=last;
  a[1].t:=1;
  for i:=2 to n do
    if a[i].t=last then a[i].t:=a[i-1].t
    else begin last:=a[i].t; a[i].t:=a[i-1].t+1; b[a[i].t]:=last; end;
  size:=a[n].t;
  new(tr[0]);
  build(tr[0],1,n);
  for i:=1 to n do
  begin
    if a[i].t>a[i-1].t then vsn[a[i].t]:=i-1;
    new(tr[i]);
    rebuild(tr[i-1],tr[i],a[i].id);
  end;

  readln(m);
  last:=0;
  for i:=1 to m do
  begin
    for j:=1 to 4 do
    begin
      read(q[j]);
      q[j]:=(q[j]+last)mod n+1;
    end;
    px;
    last:=b[solve()];
    writeln(last);
  end;
end.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值