[2017雅礼集训]day11 T1 决斗 平衡树+贪心

题目大意:有N个物品不均匀的放在N个盒子里(有些盒子可能很多,有些盒子可能没有),每个物品和每个盒子都有一个权值,要让每个盒子中只能留下一个物品,第i个盒子多余的物品溢出到第i+1个盒子中(n溢出到1)。求一种方案使得溢出完成后,使满足盒子中物品的权值大于该盒子的权值的盒子尽可能多,输出满足条件的最多盒子数。
数据范围:40%:所有物品开始放在一个盒子里。
100%:N<=5e5

40%:直接田忌赛马即可
100%:平衡树上田忌赛马。首先我们可以证明至少有一个盒子永远不会被其上一个盒子溢出。设Pi为前i个盒子包含的物品数-i (也就是往后溢出的个数),找到最小的Pm,就一定没有物品从m溢出到m+1,假设有一个区间[a,m] (或[a,n]|[1,m])中物品数大于盒子数,那么这个区间溢出的数量即为Pm-Pa<0,得证。所以我们从m+1位置把环展开成数组,维护一个物品集合,对于一个盒子若有比它大的物品就选一个比它大的最小的物品放进去,否则拿一个最小的物品放进去(类似田忌赛马),然后在把下一个盒子中的物品加入进来。这个数据结构要支持加点,求后继,删点,所以用splay。

代码

type
  tree=^treenode;
  treenode=record
    t:longint;
    f,l,r:tree;
  end;
  hash=^hashnode;
  hashnode=record
    t:longint;
    next:hash;
  end;

const maxn=500010;
var
  n,i,j,min,mini,ans,o:longint;
  a,u,v,r:array[0..maxn]of longint;
  str:tree;
  h:array[0..maxn]of hash;

procedure left(x:tree);
begin
  with x^ do
  begin
    f^.r:=l;
    if l<>nil then l^.f:=f;
    l:=f;
    f:=f^.f;
    l^.f:=x;
    if f<>nil then
    begin
      if l=f^.l then f^.l:=x
      else f^.r:=x;
    end;
  end;
end;
procedure right(x:tree);
begin
  with x^ do
  begin
    f^.l:=r;
    if r<>nil then r^.f:=f;
    r:=f;
    f:=f^.f;
    r^.f:=x;
    if f<>nil then
    begin
      if r=f^.l then f^.l:=x
      else f^.r:=x;
    end;
  end;
end;

procedure splay(var x,s:tree);
var
  p:tree;
begin
  if x=nil then exit;
  with x^ do
  begin
    while f<>nil do
    begin
      if f^.f=nil then
      begin
        if f^.l=x then right(x)
        else left(x);
      end
      else
      begin
        if f^.f^.l=f then
        begin
          if x=f^.l then right(f)
          else left(x);
          right(x);
        end
        else
        begin
          if x=f^.r then left(f)
          else right(x);
          left(x);
        end;
      end;
    end;
  end;
  s:=x;
end;

procedure ins(v:longint;var s:tree);
var
  p,last:tree;
  b:boolean;
begin
  if s=nil then
  begin
    new(s);
    with s^ do
    begin
      t:=v;
      l:=nil;
      r:=nil;
      f:=nil;
    end;
    exit;
  end;
  p:=s;
  while p<>nil do
  begin
    last:=p;
    if v>p^.t then begin b:=true; p:=p^.r; end
    else begin b:=false; p:=p^.l; end;
  end;
  new(p);
  p^.t:=v;
  p^.r:=nil;
  p^.l:=nil;
  p^.f:=last;
  if b=true then last^.r:=p
  else last^.l:=p;
  splay(p,s);
end;

procedure del(var s:tree);
var
  p,q:tree;
begin
  p:=s^.l;
  q:=s^.r;
  dispose(s);
  if (p=nil)and(q=nil) then begin s:=nil; exit; end;
  if p<>nil then
  begin
    p^.f:=nil;
    while p^.r<>nil do p:=p^.r;
    splay(p,s);
    p^.r:=q;
  end;
  if q<>nil then
  begin
    if p=nil then s:=q;
    q^.f:=p;
  end;
end;

function suc(v:longint;var s:tree):longint;
var
  p:tree;
begin
  ins(v,s);

  p:=s;
  p:=p^.r;
  if p=nil then begin del(s); exit(-1); end;
  while p^.l<>nil do p:=p^.l;
  suc:=p^.t;

  del(s);
  splay(p,str);
end;

procedure addh(x,y:longint);
var
  p:hash;
begin
  new(p);
  p^.t:=y;
  p^.next:=h[x];
  h[x]:=p;
end;

procedure adds(x:longint);
var
  p:hash;
begin
  p:=h[x];
  while p<>nil do
  begin
    ins(v[p^.t],str);
    p:=p^.next;
  end;
end;


begin
  readln(n);
  for i:=1 to n do
  begin
    read(a[i]);
    inc(r[a[i]]);
    addh(a[i],i);
  end;
  for i:=1 to n do
    read(u[i]);
  for i:=1 to n do
    read(v[i]);
  for i:=1 to n do
    r[i]:=r[i]+r[i-1];
  str:=nil;
  min:=maxn;
  for i:=1 to n do
    if r[i]-i<min then begin min:=r[i]-i; mini:=i; end;
  ans:=0;
  inc(mini);
  for i:=mini to n do
  begin
    adds(i);
    if suc(u[i],str)=-1 then begin o:=suc(0,str);  del(str); end
    else begin inc(ans); del(str); end;
  end;
  for i:=1 to mini-1 do
  begin
    adds(i);
    if suc(u[i],str)=-1 then begin o:=suc(0,str); del(str); end
    else begin inc(ans); del(str); end;
  end;
  writeln(ans);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值