poj 2131 Key Insertion

Description

As an employee of the Macrohard Company, you have been asked to implement the new data structure that would be used to store some integer keys.  
The keys must be stored in a special ordered collection that can be considered as an array A, which has an infinite number of locations, numbered starting from 1. Initially all locations are empty. The following operation must be supported by the collection: Insert(L, K), where L is the location in the array and K is some positive integer value. 
The operation must be processed as follows: 
  • If A[L] is empty, set A[L] ← K. 
  • If A[L] is not empty, perform Insert(L + 1, A[L]) and after that set A[L] ← K.

Given N integer numbers L1 , L2 , . . . , LN you have to output the contents of the array after a sequence of the following operations:  
Insert(L1 , 1)  
Insert(L2 , 2) 
. . . 
Insert(LN , N)

Input

The first line of the input file contains N --- the number of Insert operations and M --- the maximal position that can be used in the Insert operation (1 <= N <= 131 072, 1 <= M <= 131 072).  
Next line contains N integer numbers L i that describe Insert operations to be performed (1 <= Li <= M ).

Output

Output the contents of the array after a given sequence of Insert operations. On the first line print W --- the number of the greatest location that is not empty. After that output W integer numbers --- A[1], A[2], . . . , A[W ]. Output zeroes for empty locations.

 

题目大意:将n个数插入一段初始为空的序列中。给出n个数插入的位置。一个数插入序列时如果位置被占,则将该位置以后连续一段的数往后顺推一位。

//=====================================================================

考虑连续一段的要求,可以用并查集维护连续的若干块。

如果直观地做就用伸展树+即时维护连续块。每插入一个数就推一段(打上懒标记),再用并查集维护连续段。

并查集要怎么运用呢?定义一个单元的父亲是这个单元所在连续块最右端+1。很明显,初始的时候fa[i]=i,因为没有一个连续块。每次往后推都把父亲指向父亲的下个节点。怎么并两个块呢?并查集路径压缩的时候就搞掉了、

所以每次插入新的数到x单元的时候只要把从这个单元到该单元所在连续块最右端(en[x]-1)的所有数构成的子树伸展到根节点的儿子的儿子就可以了。具体操作就是找到x-1en[x]两个单元分别伸展到根和根的右儿子,这样根的右儿子的左儿子就是要往后推得子树了。打个标记放那就行了。。。

 

 

貌似可以从后往前做。因为一个数只会被后面的数影响。额。。具体操作还没想清楚。。。求大牛指教。

    //突然发现数组开小了。。。原来压着n开遇到都放末尾的情况就爆了。。。至少两倍、、

 

 

这道题最后遍历树的时候我打了人工栈,不知道为什么调用递归一直莫名202,求高手指教啊、、、

Splay Tree 打得我要多蛋疼有多蛋疼。。。再加上并查集。。。。。。再加上莫名其妙的202。。。。。。。。。

 

注意:

Splay Tree在询问操作结束后要把询问的节点伸展到根。不然不能保证复杂度。其实不管是询问,修改,加入都要伸展。

 

 

AC CODE

 

{$inline on}

{$M 20000000}

program pku_2131;

var c:array[0..300000,0..1] of longint;

a,fa,num,lazy,en,ans,stack:array[0..300000] of longint;

    //lazy是懒标记,en是并查集,a记的是插入的位置,num记的是第几个数,fa记的是伸展树上节点的父亲,stack是蛋疼的人工栈。。。

    i,z,n,tot,root,size,len,tmp,tail,now:longint;

//============================================================================

procedure prepare;inline;    //一开始先加入两个极左极右端点在后面可以省掉很多特判、

var i:longint;

begin

  readln(n,len); n:=n+len;

  for i:=1 to n+1 do en[i]:=i;

  inc(tot,2); a[1]:=0; a[2]:=n;

  c[1,1]:=2; fa[2]:=1; root:=1;

end;

//============================================================================

function geten(x:longint):longint;inline;    //并查集。(fa改成en了。。。)

begin

  if en[x]=x then exit(x);

  geten:=geten(en[x]); en[x]:=geten;

end;

//============================================================================

procedure push(x:longint);inline;    //向下推懒标记。

var g,h:longint;

begin

  g:=c[x,0]; h:=c[x,1];

  if g>0 then

  begin

    inc(a[g],lazy[x]);

    inc(lazy[g],lazy[x]);

  end;

  if h>0 then

  begin

    inc(a[h],lazy[x]);

    inc(lazy[h],lazy[x]);

  end; lazy[x]:=0;

end;

//============================================================================

procedure rotate(var root:longint; x:longint);inline;    //左旋+右旋

var y,z,p,q:longint;

begin

  y:=fa[x]; z:=fa[y];

  if c[y,0]=x then p:=0 else p:=1;

  q:=p xor 1;

  if y=root then root:=x else

    if c[z,0]=y then c[z,0]:=x else c[z,1]:=x;

  fa[x]:=z; fa[y]:=x; fa[c[x,q]]:=y;

  c[y,p]:=c[x,q]; c[x,q]:=y;

end;

//============================================================================

procedure splay(var root:longint; x:longint);inline;    //splay操作就不用多说了吧。。。

var y,z:longint;

begin

  while x<>root do

  begin

    y:=fa[x]; z:=fa[y];

    if y<>root then

      if (c[y,0]=x) xor (c[z,0]=y) then

        rotate(root,x) else rotate(root,y);

    rotate(root,x);

  end;

end;

//============================================================================

function find_left(z:longint):longint;inline;    //找到小于等于z的最大数。

var x:longint;

begin

  x:=root;

  while true do

  begin

    if lazy[x]>0 then push(x);

    if a[x]=z then exit(x);    //找到极值就可以跳出了。这程序里面会有splay操作的、

    if a[x]>z then

    begin

      if c[x,0]=0 then break;

      x:=c[x,0];

    end else

    begin

      find_left:=x;

      if c[x,1]=0 then break;

      x:=c[x,1];

    end;

  end; splay(root,x);

end;

//============================================================================

function find_right(z:longint):longint;inline;    //找到大于等于z的最大数。

var x:longint;

begin

  x:=root;

  while true do

  begin

    if lazy[x]>0 then push(x);

    if a[x]=z then exit(x);

    if a[x]<z then

    begin

      if c[x,1]=0 then break;

      x:=c[x,1];

    end else

    begin

      find_right:=x;

      if c[x,0]=0 then break;

      x:=c[x,0];

    end;

  end; splay(root,x);

end;

//============================================================================

procedure ins(x,mark:longint);    //本来加入操作写成递归了。偏弱。。。其实只要找到空的合法位置加进去就可以了,返回的时候直接对tot(新开的单元)做一次splay

var now:longint;

begin

  now:=root;

  repeat

    if lazy[now]>0 then push(now);

    if a[now]>x then

    begin

      if c[now,0]=0 then

      begin

        inc(tot);

        a[tot]:=x; num[tot]:=mark;

        fa[tot]:=now; c[now,0]:=tot;

        break;

      end else now:=c[now,0];

    end else

    begin

      if c[now,1]=0 then

      begin

        inc(tot);

        a[tot]:=x; num[tot]:=mark;

        fa[tot]:=now; c[now,1]:=tot;

        break;

      end else now:=c[now,1];

    end;

  until false;

end;

//============================================================================

procedure main;inline;

var f1,f2,x,i,l,r:longint;

begin

  for i:=1 to n-len do

  begin

    read(x); f1:=geten(x);

 

    if x<f1 then

    begin

      l:=find_left(x-1);

      r:=find_right(f1);

      splay(root,l); splay(c[root,1],r);

 

      inc(a[c[r,0]]);

      inc(lazy[c[r,0]]);

    end;

 

    ins(x,i);

    splay(root,tot);

    en[f1]:=f1+1;

 

  end;

end;

//============================================================================

procedure dfs(x:longint);    //就是在这里莫名其妙地一直202,可能是splay写挫了导致树太高了。。。求指教。

begin

  if lazy[x]>0 then push(x);

  if x>2 then

  begin

    ans[a[x]]:=num[x];

    if a[x]>tmp then tmp:=a[x];

  end;

  if c[x,0]>0 then dfs(c[x,0]);

  if c[x,1]>0 then dfs(c[x,1]);

end;

//============================================================================

procedure print;inline;

var i:longint;

begin

  writeln(tmp);

  for i:=1 to tmp do write(ans[i],' ');

end;

//============================================================================

begin

  prepare;

  main;

  tail:=1; stack[1]:=root; tmp:=0;

  while tail>0 do

  begin now:=stack[tail];

    if lazy[now]>0 then push(now);

    if now>2 then

    begin

      ans[a[now]]:=num[now];

      if a[now]>tmp then tmp:=a[now];

    end;

    if c[now,0]>0 then

    begin

      inc(tail);

      stack[tail]:=c[now,0];

      c[now,0]:=0; continue;

    end;

    if c[now,1]>0 then

    begin

      inc(tail);

      stack[tail]:=c[now,1];

      c[now,1]:=0; continue;

    end; dec(tail);

  end;

  //dfs(root);

  print;

end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值