bzoj 3689 trie树+堆

6 篇文章 0 订阅

题意:给定一个非负整数序列,两两异或,求前k小的异或值

显然,如果一个数想和其他数异或尽量小,肯定是化为二进制后,从最高位起能选这一位相同的就选能和这一位相同的,实在没有再选和这一位不同的(当这一位不同,异或后的答案一定有2^i),这一位确定后再看下一位

那么我们就对所有数的二进制建trie树,同时统计size(有几个数的这一位和当前数是相同的)

然后所有数与其他数异或值的第二小(第一小是自己异或自己)丢到一个小根堆里,当取出一个数异或值的第k‘小,就把第k’+1小丢进堆里

这样,一个异或值会被取出两次,所以我们总共需要取出2*k次,取到奇数次时输出

小tip:堆里直接是结构体时,Pascal会有大常数(会变慢),所以我们在堆里维护编号,在外面维护每个编号的相关信息即可

uses math;
type
        rec=record
            num,id,w:longint;
end;

var
        n,k,tot,sizee   :longint;
        maxn            :longint;
        i               :longint;
        b               :array[0..4000010] of rec;
        heap,a          :array[0..4000010] of longint;
        t               :array[0..4000010,0..1] of longint;
        size            :array[0..4000010] of longint;

procedure swap(var a,b:longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

procedure heap_up(i:longint);
begin
   if i=1 then exit;
   while i>1 do
   begin
      if b[heap[i]].w<b[heap[i>>1]].w then
      begin
         swap(heap[i],heap[i>>1]);
         i:=i>>1;
      end else exit;
   end;
end;

procedure heap_down(i:longint);
var
        t:longint;
begin
   while (i<<1)<=sizee do
   begin
      if b[heap[i]].w<b[heap[i<<1]].w then t:=i else t:=i<<1;
      if (i<<1)+1<=sizee then
        if b[heap[t]].w>b[heap[(i<<1)+1]].w then t:=(i<<1)+1;
      if i<>t then
      begin
         swap(heap[i],heap[t]);
         i:=t;
      end else exit;
   end;
end;

procedure insert_heap(i:longint);
begin
   inc(sizee);
   heap[sizee]:=i;
   heap_up(sizee);
end;

procedure insert(x:longint);
var
        rt,y,i:longint;
begin
   rt:=0;
   for i:=maxn downto 0 do
   begin
      if (x and (1<<i))<>0 then y:=1 else y:=0;
      if t[rt,y]=0 then
      begin
         inc(tot);
         t[rt,y]:=tot;
      end;
      rt:=t[rt,y]; inc(size[rt]);
   end;
end;

function find(id,k:longint):longint;
var
        ans,rt,i,x:longint;
begin
   ans:=0; rt:=0;
   for i:=maxn downto 0 do
   begin
      if (a[id] and (1<<i))<>0 then x:=1 else x:=0;
      if size[t[rt,x]]>=k then rt:=t[rt,x] else
      begin
         inc(ans,1<<i); dec(k,size[t[rt,x]]); rt:=t[rt,x xor 1];
      end;
   end;
   exit(ans);
end;

function work(x:longint):longint;
var
        ans:longint;
begin
   ans:=0;
   while x>0 do
   begin
      inc(ans);
      x:=x>>1;
   end;
   exit(ans);
end;

begin
   read(n,k);
   for i:=1 to n do read(a[i]);
   for i:=1 to n do maxn:=max(maxn,work(a[i]));
   for i:=1 to n do insert(a[i]);
   for i:=1 to n do
   begin
      b[i].id:=i; b[i].num:=2; b[i].w:=find(i,2);
      insert_heap(i);
   end;
   for i:=1 to (k<<1)do
   begin
      if i and 1=1 then write(b[heap[1]].w,' ');
      if b[heap[1]].num=n then
      begin
         dec(sizee); continue;
      end; 
      b[heap[1]].w:=find(b[heap[1]].id,b[heap[1]].num+1);
      inc(b[heap[1]].num);
      heap_down(1);
   end;
   writeln;
end.
——by Eirlys



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值