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;
//建树。
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机
上只有 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;
//建树。
相关文章推荐
- noi 2011 阿狸的打字机
- Codeforces 163E && HDU 4117 && Noi2011阿狸的打字机 && boj 1602
- bzoj2434 [Noi2011]阿狸的打字机 ( AC自动机 & fail树 + 树状数组 + dfs序 )
- bzoj2434(NOI2011).阿狸的打字机(AC自动机 && DFS序 && 树状数组)
- bzoj2434 阿狸的打字机NOI2011ac自动机+fail树+树状数组+dfs序详解
- [BZOJ2434][NOI2011]阿狸的打字机
- bzoj-2434: [Noi2011]阿狸的打字机
- BZOJ2434_[Noi2011]阿狸的打字机(AC自动机+树状数组+dfs序)
- [NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]
- [NOI2011]阿狸的打字机(好题!!!!)
- [NOI2011]阿狸的打字机
- BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )
- 【BZOJ】【2434】【NOI2011】阿狸的打字机
- bzoj 2434: [Noi2011]阿狸的打字机
- BZOJ2434: [Noi2011]阿狸的打字机
- 2434: [Noi2011]阿狸的打字机
- BZOJ2434 [Noi2011]阿狸的打字机 【AC自动机 + fail树 + 树状数组】
- bzoj 2434 [Noi2011]阿狸的打字机
- 【BZOJ2434】阿狸的打字机(NOI2011)-AC自动机+树状数组
- [NOI 2011]阿狸的打字机