您的位置:首页 > 其它

poj 2131 Key Insertion

2014-12-29 08:40 162 查看
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,因为没有一个连续块。每次往后推都把父亲指向父亲的下个节点。怎么并两个块呢?并查集路径压缩的时候就搞掉了、

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

 

 

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

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

 

 

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

Splay Tree

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

 


注意:


[i]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;

//===========
b4d0
=================================================================

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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: