BZOJ2434: [Noi2011]阿狸的打字机
2017-02-23 20:26
323 查看
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
Output
输出m行,其中第i行包含一个整数,表示第i个询问的答案。Sample Input
aPaPBbP3
1 2
1 3
2 3
Sample Output
21
0
HINT
1<=N<=10^51<=M<=10^5
输入总长<=10^5
Source
AC自动机+树状数组暴力做法是对于每个x,查询有多少个root-y上的点沿着fail一直跳能跳到x
那么我们fail[i]向i连边,构造出一颗树,问题转化为root-y的点有多少个在x的子树中
预处理dfs序的l,r(类似于括号序列),那么可以用树状数组维护了
进入一个点就1-l[x]都+1,退出就-1,查询就是查询l[x]-r[x]的和
将询问离线,对于y挂链表,重新跑一遍即可
#include <bits/stdc++.h> using namespace std; const int MAXN = 100010; int n, m, ans[MAXN], cnt, head[MAXN], l[MAXN], r[MAXN], tim, c[MAXN << 1], pos[MAXN], tot, Head[MAXN]; char ch[MAXN]; struct edge { int to, nxt; }e[MAXN], q[MAXN]; inline void addedge(int x, int y) { e[ ++cnt ].to = y; e[ cnt ].nxt = head[ x ]; head[ x ] = cnt; } inline int lowbit(int x) { return x & -x; } inline void modify(int x, int v) { for( ; x <= tim ; x += lowbit( x ) ) c[ x ] += v; } inline int query(int x) { int ans = 0; for( ; x ; x -= lowbit( x ) ) ans += c[ x ]; return ans; } struct ACAM { int nxt[MAXN][26], fail[MAXN], fa[MAXN]; int cnt, now, Q[MAXN]; ACAM() { for( int i = 0 ; i < 26 ; i++ ) nxt[ 0 ][ i ] = 1; now = cnt = 1; } inline void build() { scanf( "%s", ch + 1 ); n = strlen( ch + 1 ); for( int i = 1 ; i <= n ; i++ ) { if( ch[ i ] == 'P' ) { pos[ ++tot ] = now; } else if( ch[ i ] == 'B' ) now = fa[ now ]; else { if( !nxt[ now ][ ch[ i ] - 'a' ] ) fa[ nxt[ now ][ ch[ i ] - 'a' ] = ++cnt ] = now; now = nxt[ now ][ ch[ i ] - 'a' ]; } } } inline void buildfail() { int ql = 0, qr = 1; Q[ 1 ] = 1; while( ql ^ qr ) { int x = Q[ ++ql ]; for( int i = 0 ; i < 26 ; i++ ) if( nxt[ x ][ i ] ) { int k = fail[ x ]; while( !nxt[ k ][ i ] ) k = fail[ k ]; fail[ nxt[ x ][ i ] ] = nxt[ k ][ i ]; Q[ ++qr ] = nxt[ x ][ i ]; } } for( int i = 2 ; i <= cnt ; i++ ) addedge( fail[ i ], i ); } inline void solve() { now = 1; int tmp = 0; for( int i = 1 ; i <= n ; i++ ) if( ch[ i ] == 'B' ) modify( l[ now ], -1 ), now = fa[ now ]; else if( ch[ i ] == 'P' ) { tmp++; for( int j = Head[ tmp ] ; j ; j = q[ j ].nxt ) { int x = pos[ q[ j ].to ]; ans[ j ] = query( r[ x ] ) - query( l[ x ] - 1 ); } } else now = nxt[ now ][ ch[ i ] - 'a' ], modify( l[ now ], 1 ); } }AC; inline void dfs(int x) { l[ x ] = ++tim; for( int i = head[ x ] ; i ; i = e[ i ].nxt ) dfs( e[ i ].to ); r[ x ] = ++tim; } int main() { AC.build(); AC.buildfail(); dfs( 1 ); scanf( "%d", &m ); for( int i = 1 ; i <= m ; i++ ) { int x, y; scanf( "%d%d", &x, &y ); q[ i ].to = x; q[ i ].nxt = Head[ y ]; Head[ y ] = i; } AC.solve(); for( int i = 1 ; i <= m ; i++ ) printf( "%d\n", ans[ i ] ); return 0; }
相关文章推荐
- 【AC自动机】 BZOJ 2434 [Noi2011]阿狸的打字机
- Bzoj2434 [Noi2011]阿狸的打字机
- 2434: [Noi2011]阿狸的打字机 - BZOJ
- BZOJ2434: [Noi2011]阿狸的打字机
- BZOJ_2434_[NOI2011]_阿狸的打字机_(AC自动机+dfs序+树状数组)
- BZOJ 2434: [Noi2011]阿狸的打字机(fail树+树状数组)
- bzoj 2434: [Noi2011]阿狸的打字机
- 【Bzoj 2434】[NOI 2011] 阿狸的打字机
- BZOJ2434:[NOI2011]阿狸的打字机——题解
- bzoj2434 [Noi2011]阿狸的打字机
- BZOJ 2434: [Noi2011]阿狸的打字机
- BZOJ 2434 [Noi2011]阿狸的打字机(AC自动机)
- [BZOJ2434][NOI2011]阿狸的打字机(AC自动机+树状数组)
- 【bzoj2434】[Noi2011]阿狸的打字机 AC自动机+fail树+dfs序+树状数组
- ●BZOJ 2434: [Noi2011]阿狸的打字机
- BZOJ 2434: [Noi2011]阿狸的打字机
- bzoj 2434: [Noi2011]阿狸的打字机
- 【BZOJ 2434】[Noi2011]阿狸的打字机 Ac自动机+树状数组+dfs序
- [BZOJ2434][NOI2011]阿狸的打字机-AC自动机
- bzoj2434: [Noi2011]阿狸的打字机 trie+线段树