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


    2434: [Noi2011]阿狸的打字机

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 3726  Solved: 2043
    [Submit][Status][Discuss]

    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

    aPaPBbP

    3

    1 2

    1 3

    2 3

    Sample Output

    2

    1

    0

    HINT

     1<=N<=10^5


    1<=M<=10^5

    输入总长<=10^5

    Source

    【题解】

    http://blog.csdn.net/lych_cys/article/details/50646799

    写得太赞啦,摘一些要点

    首先不难模拟操作建AC自动机

    后缀数组找子串是找后缀的前缀,而AC自动机找子串是看前缀的后缀。

    本题中要找x在y中出现的次数

    即要问x在y的前缀的后缀中出现的次数

    AC自动机上y所在链上一个点u的fail指向x的末尾v,就说明y的一个1...v前缀的后缀是x

    问题转化为求y所在链有多少个fail指向x的末尾

    不妨建立一个fail树,将AC自动机根0道y链末尾结点权值变为1,答案即为x末尾在fail树中子树的权值

    可以看到需要修改路径、查询子树

    树链剖分 + dfs序用sgt维护?

    NO!

    离线读入所有的x,y,按y末尾在AC自动机中节点编号大小排序,询问次序变成了建trie树的顺序

    这样我们重走一遍建AC自动机的过程,每次进入一个点,该点+1,每次离开一个点,该点-1,同时检查询问,直接回答即可。

    这样我们只需要支持子树查询和单点修改,dfs序 + 树状数组!

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <cstdlib>
      5 #include <algorithm>
      6 #define lowbit(x) ((-1 * (x)) & x)
      7 inline void read(int &x)
      8 {
      9     x = 0;char ch = getchar(), c = ch;
     10     while(ch < '0' || ch > '9') c = ch, ch = getchar();
     11     while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
     12     if(c == '-') x = -x;
     13 }
     14 const int INF = 0x3f3f3f3f;
     15 const int MAXN = 200000 + 5;
     16 int m,ch[MAXN][30],cnt,tot,rank[MAXN],fail[MAXN],fa[MAXN],last[MAXN],tag[MAXN],q[MAXN], he, ta;
     17 char s[MAXN];
     18 void insert()
     19 {
     20     int now = 0;
     21     for(register int i = 1;s[i] != '';++ i)
     22     {
     23         if(s[i] == 'B') now = fa[now];
     24         else if(s[i] == 'P') ++ tag[now], rank[++ tot] = now;
     25         else if(ch[now][s[i] - 'a' + 1]) now = ch[now][s[i] - 'a' + 1];
     26         else fa[++ cnt] = now, now = ch[now][s[i] - 'a' + 1] = cnt;
     27     }
     28 }
     29 void build()
     30 {
     31     he = ta = 0;
     32     for(int i = 1;i <= 26;++ i) if(ch[0][i]) q[ta ++] = ch[0][i];
     33     while(he < ta)
     34     {
     35         int now = q[he ++];
     36         for(int i = 1;i <= 26;++ i)
     37         {
     38             int u = ch[now][i], v = fail[now];
     39             if(!u) continue;
     40             q[ta ++] = u;
     41             while(v && !ch[v][i]) v = fail[v];
     42             fail[u] = ch[v][i];
     43             last[u] = tag[fail[u]] ? fail[u] : last[fail[u]];
     44         }
     45     }
     46 }
     47 struct Edge
     48 {
     49     int u,v,nxt;
     50     Edge(int _u, int _v, int _nxt){u = _u;v = _v;nxt = _nxt;}
     51     Edge(){}
     52 }edge[MAXN << 1];
     53 int head[MAXN], ecnt, l[MAXN], r[MAXN], w[MAXN << 1], tot_tree;
     54 inline void insert(int a, int b)
     55 {
     56     edge[++ecnt] = Edge(a,b,head[a]);
     57     head[a] = ecnt;
     58 }
     59 void dfs(int u, int pre)
     60 {
     61     l[u] = ++ tot_tree;
     62     for(register int pos = head[u];pos != -1;pos = edge[pos].nxt)
     63     {
     64         int v = edge[pos].v;
     65         if(v == pre) continue;
     66         dfs(v, u);
     67     }
     68     r[u] = tot_tree;
     69 }
     70 void make_tree()
     71 {
     72     memset(head, -1, sizeof(head));
     73     for(register int i = 1;i <= cnt;++ i) 
     74         insert(fail[i], i), insert(i, fail[i]);
     75     dfs(0, -1);
     76 }
     77 void modify(int x, int k)
     78 {
     79     for(;x <= tot_tree;x += lowbit(x)) w[x] += k;
     80 }
     81 int ask(int x)
     82 {
     83     int sum = 0;
     84     for(;x;x -= lowbit(x)) sum += w[x];
     85     return sum;        
     86 }
     87 struct Node
     88 {
     89     int x,y,rank;
     90 }node[MAXN];
     91 bool cmp(Node a, Node b)
     92 {
     93     return a.y < b.y;
     94 }
     95 int ans[MAXN];
     96 void solve()
     97 {
     98     int now = 0, t = 1;
     99     for(register int i = 1;s[i] != '';++ i)
    100         if(s[i] == 'B') modify(l[now], - 1), now = fa[now];
    101         else if(s[i] == 'P') while(now == node[t].y) ans[node[t].rank] = ask(r[node[t].x]) - ask(l[node[t].x] - 1), ++ t;
    102         else now = ch[now][s[i] - 'a' + 1], modify(l[now], 1);
    103 }
    104 int main()
    105 {
    106     scanf("%s", s + 1);
    107     insert();
    108     build();
    109     make_tree();
    110     read(m);
    111     for(register int i = 1;i <= m;++ i) read(node[i].x), read(node[i].y), node[i].rank = i, node[i].x = rank[node[i].x], node[i].y = rank[node[i].y];
    112     std::sort(node + 1, node + 1 + m, cmp);
    113     solve();
    114     for(register int i = 1;i <= m;++ i) printf("%d
    ", ans[i]);
    115     return 0;
    116 }
    BZOJ2434
  • 相关阅读:
    元素单侧阴影效果
    有关动画的总结
    吸底效果
    使用seajs来引入js代码
    引用POPUI来实现弹窗效果,且弹窗中的内容可以点击事件
    Js文本溢出自动添加省略号ellipsis
    line-hieght与vertical-align的区别与联系
    实现文本框中输入限制字数的效果
    下拉菜单的实现
    基本SQL命令 (1.SQL命令使用规则/2.库管理/3.表管理/4.表记录管理/5.更改库,库的默认字符集/6.连接数据库的过程/7.数据类型)
  • 原文地址:https://www.cnblogs.com/huibixiaoxing/p/8324370.html
Copyright © 2020-2023  润新知