• luogu解题报告:P2414[NOI2011]阿狸的打字机


    https://www.luogu.org/problem/show?pid=2414

    题目背景

    阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。

    题目描述

    打字机上只有28个按键,分别印有26个小写英文字母和’B’、’P’两个字母。经阿狸研究发现,这个打字机是这样工作的:

    • 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。

    • 按一下印有’B’的按键,打字机凹槽中最后一个字母会消失。

    • 按一下印有’P’的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

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

    a aa ab

    我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1x,yn),打字机会显示第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 != ''; ++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
    ", ans[i]);
        return 0;
    }
  • 相关阅读:
    SIMD函数整理:01 《PC平台新技术MMX(上册):开发编程指南》第8章 MMX编码技术
    [维多利亚2 MOD] RecoverMingV(Vic2版复明) V1.1.2(201246更新),兼容AHD 2.3beta
    使用GetLogicalProcessorInformation获取逻辑处理器的详细信息(NUMA节点数、物理CPU数、CPU核心数、逻辑CPU数、各级Cache)
    [TurboC++] 如何在DOS下的16位C++编译器中使用CPUID指令获取CPU信息
    深入探讨用位掩码代替分支(9):测试成绩总结
    深入探讨用位掩码代替分支(7):MMX指令集速度测试
    [维多利亚2 MOD] 中国地区省份区划补丁 V2.0。支持 AHD(阋墙)
    深入探讨用位掩码代替分支(5):C#2010速度测试
    x264编码参数大测试:07 subme与crf(小结)
    深入探讨用位掩码代替分支(2):汇编代码分析
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684345.html
Copyright © 2020-2023  润新知