luogu解题报告:P2414[NOI2011]阿狸的打字机
2017-02-19 10:19
190 查看
https://www.luogu.org/problem/show?pid=2414
输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
按一下印有’B’的按键,打字机凹槽中最后一个字母会消失。
按一下印有’P’的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
输出样例#1:
由于涉及多个串,不难想到用AC自动机处理。考虑构建了Fail指针后形成的fail树。对于一个询问 (x,y),也就是在fail树中y的子树中寻找x的前缀。然而在fail树中维护每一个串的前缀是不可能的,因而只能反其道而行之。
在dfs遍历Trie的过程中维护当前链是容易的(即在一个树状数组中使得为1的元素是当前链上的)。当我们处理到节点y时,对于任何一个询问 (x,y),只需要查看x的子树中1的个数,后者可以用dfs序处理。复杂度为 O(LΣ+(L+N)lgL)。
题目背景
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。题目描述
打字机上只有28个按键,分别印有26个小写英文字母和’B’、’P’两个字母。经阿狸研究发现,这个打字机是这样工作的:输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
按一下印有’B’的按键,打字机凹槽中最后一个字母会消失。
按一下印有’P’的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入
aPaPBbP,纸上被打印的字符如下:
a aa ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
输入输出格式
输入格式:
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
输出格式:
输出m行,其中第i行包含一个整数,表示第i个询问的答案。输入输出样例
输入样例#1:aPaPBbP 3 1 2 1 3 2 3
输出样例#1:
2 1 0
说明
对于100%的数据,n<=100000,m<=100000,第一行总长度<=100000。思路与解
AC自动机+dfs序+树状数组。由于涉及多个串,不难想到用AC自动机处理。考虑构建了Fail指针后形成的fail树。对于一个询问 (x,y),也就是在fail树中y的子树中寻找x的前缀。然而在fail树中维护每一个串的前缀是不可能的,因而只能反其道而行之。
在dfs遍历Trie的过程中维护当前链是容易的(即在一个树状数组中使得为1的元素是当前链上的)。当我们处理到节点y时,对于任何一个询问 (x,y),只需要查看x的子树中1的个数,后者可以用dfs序处理。复杂度为 O(LΣ+(L+N)lgL)。
Code
#include <bits/stdc++.h> using namespace std; char str[100005]; //#define Debug struct graph { struct node { int to, next, id; } edge[100005]; int head[100005], top; int L[100005], R[100005], d; graph():top(0) { memset(head, 0, sizeof head); d = 0; } void push(int i, int j, int id = 0) { edge[++top].to = j; edge[top].next = head[i]; edge[top].id = id; head[i] = top; } void dfs(int nd = 1) { L[nd] = ++d; for (int i = head[nd]; i; i = edge[i].next) dfs(edge[i].to); R[nd] = d; } } query; struct acm { struct node { int chl[26]; int finished, fail, fa; node() {memset(chl, 0, sizeof chl); } } tree[100005]; int top, root; queue<int> que; int str2pos[100005]; graph failTree; acm():top(1), root(1){} void init(const char *str) { int nd = root; int cnt = 0; for (; *str != '\0'; ++str) { if (*str == 'P') tree[nd].finished = ++cnt, str2pos[cnt] = nd; else if (*str == 'B') nd = tree[nd].fa; else { tree[nd].chl[*str-'a'] = ++top; tree[top].fa = nd; nd = top; } } } void buildFail() { tree[root].fail = 0; for (que.push(root); !que.empty(); que.pop()) { int k = que.front(); for (int i = 0; i < 26; i++) if (tree[k].chl[i]) { int t = tree[k].fail; while (t && !tree[t].chl[i]) t = tree[t].fail; tree[tree[k].chl[i]].fail = t?tree[t].chl[i]:root; failTree.push(tree[tree[k].chl[i]].fail, tree[k].chl[i]); que.push(tree[k].chl[i]); } } failTree.dfs(); } void dfs(int nd, char c, int tab = 0) { if (!nd) return; for (int i = 1; i <= tab; i++) putchar(' '); cout << nd << "-->" << c << "(" << tree[nd].finished << "), fail = " << tree[nd].fail << endl; for (int i = 0; i < 26; i++) dfs(tree[nd].chl[i], i+'a', tab+2); } } Acm; struct bit { int c[100005]; bit(){memset(c, 0, sizeof c);} inline int lowbit(int i) {return i&-i;} void modify(int i, int j) { for (; i < 100005; i += lowbit(i)) c[i] += j; } int sum(int i) { int ans = 0; for (; i; i -= lowbit(i)) ans += c[i]; return ans; } inline int sum(int l, int r) { return l <= r ? sum(r)-sum(l-1) : 0; } } Bit; int x[100005], y[100005], n, ans[100005]; void init() { scanf("%s", str); scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d%d", &x[i], &y[i]); query.push(y[i], x[i], i); } Acm.init(str); Acm.buildFail(); #ifdef Debug Acm.dfs(1, 'R'); #endif } void solve(int nd) { #ifdef Debug cout << "Solving: " << nd << endl; #endif Bit.modify(Acm.failTree.L[nd], 1); if (Acm.tree[nd].finished) { for (int i = query.head[Acm.tree[nd].finished]; i; i = query.edge[i].next) { int to = Acm.str2pos[query.edge[i].to], id = query.edge[i].id; #ifdef Debug cout << "--From : " << to << " ; id = " << id << endl; #endif ans[id] = Bit.sum(Acm.failTree.L[to], Acm.failTree.R[to]); } } for (int i = 0; i < 26; i++) if (Acm.tree[nd].chl[i]) solve(Acm.tree[nd].chl[i]); Bit.modify(Acm.failTree.L[nd], -1); } int main() { freopen("noi2011_type.in", "r", stdin); freopen("noi2011_type.out", "w", stdout); init(); solve(1); for (int i = 1; i <= n; i++) printf("%d\n", ans[i]); return 0; }
相关文章推荐
- 【AC自动机】【树状数组】【dfs序】洛谷 P2414 [NOI2011]阿狸的打字机 题解
- bzoj 2434 [Noi2011]阿狸的打字机 [AC自动机+树状数组]
- bzoj 2434 [Noi2011]阿狸的打字机(fail树+离线处理+BIT)
- bzoj2434 阿狸的打字机[NOI2011] AC自动机+树状数组
- BZOJ 2434 NOI 2011 阿狸的打字机 AC自动机构造fail树
- noi 2011 阿狸的打字机
- 【BZOJ2434】[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组
- bzoj2434 [Noi2011]阿狸的打字机(AC自动机+fail树+树状数组)
- BZOJ 2434: [Noi2011]阿狸的打字机
- bzoj 2434 (NOI2011)阿狸的打字机 【AC自动机】【树状数组】【DFS序】
- BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )
- [NOI2011]阿狸的打字机(好题!!!!)
- NOI2011阿狸的打字机(fail树+DFS序)
- BZOJ 2434 [Noi2011]阿狸的打字机 - AC自动机+树状数组
- BZOJ2434: [Noi2011]阿狸的打字机
- 【BZOJ 2434】 [Noi2011]阿狸的打字机 fail树+树状数组
- NOI2011 阿狸的打字机(BZOJ2434) 题解
- [NOI2011阿狸的打字机]
- 【bzoj2434】[Noi2011]阿狸的打字机 AC自动机+Dfs序+树状数组
- BZOJ2434_[Noi2011]阿狸的打字机(AC自动机+树状数组+dfs序)