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-1和en[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.
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-1和en[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.
相关文章推荐
- poj 2131 Key Insertion
- HDU:1063 POJ:1001 Exponentiat…
- POJ 2247 Humble Numbers
- ZZULI_SummerPractice(3) POJ 12…
- POJ 3278 Catch That Cow
- POJ 1018 Communication System
- Poj 2886 Who Gets the Most Candi…
- ZZULI_SummerPractice(4) POJ 132…
- POJ 1256 Anagram(全排列问题)
- POJ 2236 Wireless Network
- POJ 1050 To the Max
- POJ 1753 Flip Game
- POj 1017 Packets
- POj 1013 Counterfeit Dollar
- POJ 1664 放苹果
- POJ 1002 487-3279
- POj 2255
- POJ 3185 The Water Bowls
- ZZULI_SummerPractice(3) POJ 3598…
- ZZULI_SummerPractice(4) POJ 3302…