noi 2011 阿狸的打字機

【問題描述】

阿狸喜歡收藏各種稀奇古怪的東西,最近他淘到一臺老式的打字機。打字機

上只有 28 個按鍵,分別印有 26 個小寫英文字母和'B''P'兩個字母。

經阿狸研究發現,這個打字機是這樣工作的:

輸入小寫字母,打字機的一個凹槽中會加入這個字母( P 前凹槽中至

少有一個字母)

按一下印有'B'的按鍵,打字機凹槽中最後一個字母會消失。

按一下印有'P'的按鍵,打字機會在紙上打印出凹槽中現有的所有字母並

換行,但凹槽中的字母不會消失(保證凹槽中至少有一個字母)。

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

a

aa

ab

我們把紙上打印出來的字符串從 1 開始順序編號,一直到 n。打字機有一個

非常有趣的功能,在打字機中暗藏一個帶數字的小鍵盤,在小鍵盤上輸入兩個數

(x,y)(其中 1x,yn),打字機會顯示第 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

noi <wbr>2011 <wbr>阿狸的打字機

 

 

用失敗指針反向建樹的原因網上很多了,這裏講下後面處理的細節。

建完樹只是找到了一個新思路,但是還是無法解決如何在以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[n]:=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;    //建樹。
  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章