zoukankan      html  css  js  c++  java
  • [BZOJ2434][Noi2011]阿狸的打字机

    [BZOJ2434][Noi2011]阿狸的打字机

    试题描述

    阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
    经阿狸研究发现,这个打字机是这样工作的:
    l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
    l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
    l 按一下印有'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个询问的答案。

    输入示例

    aPaPBbP
    3
    1 2
    1 3
    2 3

    输出示例

    2
    1
    0

    数据规模及约定

    1<=N<=10^5

    1<=M<=10^5

    输入总长<=10^5

    题解

    先构建一个AC自动机,建立好 Trie 树和 Fail 树。

    不难想到一个暴力:对于每个询问(x, y),从 Trie 树中 y - root 的每个节点出发顺着 Fail 边走,能够到达 x 结点的路径条数即为答案。

    不妨换个角度思考:对于询问(x, y),在 Fail 树中,以节点 x 为根的子树中满足“条件”的节点个数。一个节点满足“条件”当且仅当该节点在 Trie 树中的 y - root 上。

    既然是处理子树问题,就可以用dfs序将其转化为序列问题了。

    求一遍 Fail 树的dfs序,重新遍历一遍AC自动机,遇到 Trie 树上的节点 u 时将dfs序上位于 L[u] 的位置加 1,若是从节点 u 向上退格则将位于 L[u] 的位置减 1. 离线处理每个询问,在当前结点为 y 的时候,把所有(xi, y)的询问处理掉,其答案为区间 [ L[xi], R[xi] ] 的和。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <stack>
    #include <vector>
    #include <queue>
    #include <cstring>
    #include <string>
    #include <map>
    #include <set>
    using namespace std;
    
    const int BufferSize = 1 << 16;
    char buffer[BufferSize], *Head, *tail;
    inline char Getchar() {
        if(Head == tail) {
            int l = fread(buffer, 1, BufferSize, stdin);
            tail = (Head = buffer) + l;
        }
        return *Head++;
    }
    int read() {
        int x = 0, f = 1; char c = Getchar();
        while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
        while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
        return x * f;
    }
    
    #define maxn 100010
    #define maxs 26
    int n, q, ans[maxn], pos[maxn], cnt;
    char S[maxn];
    
    int m, head[maxn], next[maxn], to[maxn];
    void AddEdge(int a, int b) {
    //	printf("%d -> %d
    ", a, b);
    	to[++m] = b; next[m] = head[a]; head[a] = m;
    	return ;
    }
    int ToT = 1, f[maxn], fa[maxn], ch[maxn][maxs], Q[maxn], hd, tl;
    void GetFail() {
    	hd = tl = 0; f[1] = 1;
    	for(int i = 0; i < maxs; i++) if(ch[1][i]) {
    		int u = ch[1][i];
    		f[u] = 1; Q[++tl] = u; AddEdge(1, u);
    	}
    	while(hd < tl) {
    		int r = Q[++hd];
    		for(int i = 0; i < maxs; i++) if(ch[r][i]) {
    			int u = ch[r][i];
    			int j = f[r];
    			while(j > 1 && !ch[j][i]) j = f[j];
    			f[u] = ch[j][i] ? ch[j][i] : 1;
    			AddEdge(f[u], u);
    			Q[++tl] = u;
    		}
    	}
    	return ;
    }
    int tot, L[maxn], R[maxn];
    void build(int u) {
    	L[u] = ++tot;
    	for(int e = head[u]; e; e = next[e]) build(to[e]);
    	R[u] = tot;
    	return ;
    }
    
    int headq[maxn], nextq[maxn], qx[maxn];
    
    int C[maxn];
    void add(int x, int v) { for(; x <= tot; x += x & -x) C[x] += v; return ; }
    int sum(int x) { int res = 0; for(; x; x -= x & -x) res += C[x]; return res; }
    
    int main() {
    //	freopen("data.in", "r", stdin);
    //	freopen("data.out", "w", stdout);
    	char tc = Getchar();
    	while(!isalpha(tc)) tc = Getchar();
    	int u = 1;
    	while(isalpha(tc)) {
    		if('a' <= tc && tc <= 'z') {
    			int d = tc - 'a';
    			if(!ch[u][d]) ch[u][d] = ++ToT, fa[ch[u][d]] = u;
    			u = ch[u][d];
    		}
    		if(tc == 'P') pos[++cnt] = u;
    		if(tc == 'B' && fa[u]) u = fa[u];
    		S[++n] = tc;
    		tc = Getchar();
    	}
    	
    //	printf("%d
    ", ToT);
    //	for(int i = 1; i <= cnt; i++) printf("%d ", pos[i]); putchar('
    ');
    	GetFail(); build(1);
    	q = read();
    	for(int i = 1; i <= q; i++) {
    		int x = pos[read()], y = pos[read()];
    		nextq[i] = headq[y]; headq[y] = i; qx[i] = x;
    	}
    	u = 1;
    	for(int i = 1; i <= n; i++) {
    		if('a' <= S[i] && S[i] <= 'z') u = ch[u][S[i]-'a'], add(L[u], 1);
    		if(S[i] == 'B' && fa[u]) add(L[u], -1), u = fa[u];
    		if(S[i] == 'P')
    			for(int i = headq[u]; i; i = nextq[i]) ans[i] = sum(R[qx[i]]) - sum(L[qx[i]]-1);
    	}
    	
    	for(int i = 1; i <= q; i++) printf("%d
    ", ans[i]);
    	
    	return 0;
    }
    
  • 相关阅读:
    c# AOP OnExceptionAspect
    标签AttributeUsage 使用
    wpf gridview 滚动懒加载
    vue同级传值
    vue vant解决浏览器刷新导航栏跳转至首页的两个方法
    window.open
    获取data-*属性值
    js点击页面其他地方如何隐藏div元素菜单
    vuecli4升级与搭建项目
    在线转rem
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5448699.html
Copyright © 2011-2022 走看看