您的位置:首页 > 其它

noi 2011 阿狸的打字机

2014-12-29 08:35 148 查看
【问题描述】

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机

上只有 28

个按键,分别印有 26

个小写英文字母和'B'、'P'两个字母。

经阿狸研究发现,这个打字机是这样工作的:



输入小写字母,打字机的一个凹槽中会加入这个字母(按
P
前凹槽中至

少有一个字母)。



按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。



按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并

换行,但凹槽中的字母不会消失(保证凹槽中至少有一个字母)。

例如,阿狸输入 aPaPBbP,纸上被打印的字符如下:

a

aa

ab

我们把纸上打印出来的字符串从 1

开始顺序编号,一直到 n。打字机有一个

非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数

(x,y)(其中
1≤x,y≤n),打字机会显示第
x
个打印的字符串在第 y

个打印的字符串

中出现了多少次。

阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助

他么?

【输入格式】

从文件 type.in

中读入数据。

输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。

第二行包含一个整数 m,表示询问个数。

接下来 m

行描述所有由小键盘输入的询问。其中第 i
行包含两个整数 x,
y,

表示第 i

个询问为(x,
y)。

【输出格式】

输出到文件 type.out

中。

输出 m
行,其中第 i

行包含一个整数,表示第 i

个询问的答案。

【样例输入】

aPaPBbP

3

1
2

1
3

2
3

【样例输出】

2

1

0





 

 

用失败指针反向建树的原因网上很多了,这里讲下后面处理的细节。

建完树只是找到了一个新思路,但是还是无法解决如何在以x串最后一个字符所在的节点为根的子树中找到包含多少个y串中的节点。如果你已经把每个节点所在的串标记了,怎么寻找呢?

再考虑,一棵树中以某节点为根的子树能满足那些性质呢?dfs序是连续的。所以我们可以先求出树的dfs序,确定以每个节点为根的子树所对应的区间。

然后呢?如果询问的是(x,y),那么我们就想办法x串最后一个字符所在节点为根的子树中的y串上的节点独立出来。一个想法就是把它们赋成1,其他的点赋成0,然后就是树状数组或者线段树的区间求和操作了。

但是我们不可能对每个询问都清空树上面的标志在打上新的标志。所以就想:对于每次标记完的树都要尽可能地利用这些标记。显然。对于所有询问我们就可以把每一个y标记完的树,处理所有对应的(x[i],y)询问。

由于输入数据的特殊性。。。只要跟着输入的串在树上一步一步地走就可以了:

1.碰到B取消标志往上走。(trie树上。标志是映射到dfs序上的标志)

2.碰到P处理询问。

3.否则继续往下走并打上标记。(同1)

 

AC
CODE


 

program noi_2011_day1_type;

var g1,g2,g3,next,last,en,tail:array[1..100000] of longint;

   
e,q,fa,ps,pt,fail,ans:array[1..100000] of longint;

   
trie:array[1..100000,1..26] of longint;

   
c:array[1..200000] of longint;

   
s:array[1..100000] of char;

   
tot,size,len,len1,len2,l,r,n:longint;

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

procedure ins(x,y:longint);

begin

  inc(len1); g1[len1]:=y;

  next[len1]:=en[x]; en[x]:=len1;

end;

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

procedure
insert(x,y,z:longint);   
//本来打算对询问排序一下一起搞的。。看到网上建图的奇葩做法顿觉我弱爆了。。。

begin

  inc(len2); g2[len2]:=y; g3[len2]:=z;

  last[len2]:=tail[x]; tail[x]:=len2;

end;

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

procedure
add(x,y:longint);   
//树状数组的修改操作。(添加和取消标号一起搞,加上±1)

begin

  while x<=size do

  begin

   
inc(c[x],y);

    inc(x,x and
-x);

  end;

end;

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

function
sum(x:longint):longint;   
//树状数组的求和操作。

begin sum:=0;

  while x>0 do

  begin

   
inc(sum,c[x]);

    dec(x,x and
-x);

  end;

end;

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

procedure init;

var now,tmp:longint;

   
ch:char;

begin

  read(ch); tot:=1; now:=1; n:=0;

  while ch in ['B','P','a'..'z'] do

  begin

    inc(len);
s[len]:=ch;

    if ch='B'
then now:=fa[now] else

    if ch='P'
then

    begin

     
inc(n); e
:=now;

    end
else

    begin

     
tmp:=ord(ch)-ord('a')+1;

     
if trie[now,tmp]<>0 then
now:=trie[now,tmp]
else   
//建立trie树。

     
begin

       
inc(tot); trie[now,tmp]:=tot;

       
fa[tot]:=now; now:=tot;

     
end;

    end;
read(ch);

  end;

end;

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

procedure
get_fail;   
//生成失败指针。顺便反向建树。

var g,h,i,j,now:longint;

   
tmp:char;

begin

  l:=1; r:=0; fail[1]:=0;

  for i:=1 to 26
do   
//后来写的一个AC自动机引入一个-1点貌似在求失败指针的时候更简洁。

  begin

    if
trie[1,i]<>0 then

    begin

     
inc(r); q[r]:=trie[1,i];

     
fail[q[r]]:=1; ins(1,q[r]);  //建树。

    end;

  end;

  while l<=r do

  begin g:=r;

    for h:=l to
g do

    begin
now:=q[h];

     
for i:=1 to 26 do

     
begin

       
if trie[now,i]<>0 then

       
begin

         
inc(r); q[r]:=trie[now,i]; j:=fail[now];

         
while j<>0 do

           
if trie[j,i]<>0 then

           
begin

             
fail[q[r]]:=trie[j,i];

             
ins(trie[j,i],q[r]);
break;   
//建树。

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