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

    【BZOJ2434】[NOI2011]阿狸的打字机

    题面

    bzoj

    洛谷

    题解

    我们先想一下最暴力是怎么搞的

    把$AC$自动机建好,每一个节点,从$y$串的结尾节点往上跳它的父亲,

    和普通的$AC$自动机一样跳就好了

    然而这个可以优化一下

    我们将所有询问离线

    每个串统计一次其他串对它的贡献

    就可以有$70pts$了

    $70pts$代码

    #include <iostream> 
    #include <cstdio> 
    #include <cstdlib> 
    #include <cstring> 
    #include <cmath> 
    #include <algorithm> 
    #include <queue> 
    using namespace std;
    const int MAX_N = 2e5 + 5; 
    char s[MAX_N]; 
    int N, tot, nd[MAX_N], ans[MAX_N], sum[MAX_N]; 
    struct Trie { int ch[26], fail, fa, End; } t[MAX_N]; 
    void build() {
        static queue<int> que; 
        for (int i = 0; i < 26; i++) if (t[0].ch[i]) que.push(t[0].ch[i]), t[t[0].ch[i]].fail = 0; 
        while (!que.empty()) { 
            int o = que.front(); que.pop(); 
            for (int i = 0; i < 26; i++) {
                if (t[o].ch[i]) t[t[o].ch[i]].fail = t[t[o].fail].ch[i], que.push(t[o].ch[i]);
                else t[o].ch[i] = t[t[o].fail].ch[i]; 
            } 
        } 
    }
    int query(int y) { 
        int res = 0;
        for (int o = nd[y]; o; o = t[o].fa)
            for (int x = o; x; x = t[x].fail) if (t[x].End) ++sum[t[x].End]; 
        return res; 
    } 
    struct Query { int x, y, id; } q[MAX_N]; 
    bool operator < (const Query l, const Query r) { return l.y < r.y; } 
    
    int main () { 
        scanf("%s", s + 1);
        for (int o = 0, i = 1, l = strlen(s + 1); i <= l; i++) { 
            if ('a' <= s[i] && s[i] <= 'z') {
                if (!t[o].ch[s[i] - 'a']) t[o].ch[s[i] - 'a'] = ++tot, t[tot].fa = o; 
                o = t[o].ch[s[i] - 'a']; 
            } 
            if (s[i] == 'B') o = t[o].fa; 
            if (s[i] == 'P') nd[++N] = o, t[o].End = N; 
        }
        build(); 
        int M; scanf("%d", &M); 
        for (int i = 1; i <= M; i++) scanf("%d", &q[i].x), scanf("%d", &q[i].y), q[i].id = i;
        sort(&q[1], &q[M + 1]); 
        for (int i = 1, j = 1; i <= M; i = j) { 
            query(q[i].y); 
            while (q[j].y == q[i].y) ans[q[j].id] = sum[q[j].x], ++j; 
            fill(&sum[1], &sum[N + 1], 0); 
        } 
        for (int i = 1; i <= M; i++) printf("%d
    ", ans[i]); 
        return 0; 
    } 
    

    然后我们想一下这个过程:

    每一个点往上跳,如果可以跳到一个点,是其他字符串的$End$节点就统计贡献

    所以对于一个点,它跳到它被匹配的模式串的$End$点的次数,就是答案

    换句话说

    对于每个模式串,就是要统计有多少个文本串能跳到它。

    这个就好解决一些了,

    发现对于每个点,它对应的$fail$只有一个

    所以我们将点$i$与$fail_i$连边,

    在新的树上统计:

    每次将串$i$的链上加一,然后将询问离线统计贡献。

    但是这样做还是只有$70pts$,

    因为还是有很多状态被重复统计,

    那么如何继续优化呢?

    接下来的做法就很巧妙了:

    我们保留原来的$trie$和根据$fail$新建的树

    在新树上预处理$dfs$序,

    然后$dfs$遍历一遍原$trie$,入栈时将这个点$+1$

    出栈时$-1$

    如果这个点有结尾的串,就对这个串统计贡献,

    因为我们$dfs$回答询问时只会有那一个串被统计

    所以我们的做法是对的。

    如有什么不理解的地方,可以参见代码

    代码

    #include <iostream> 
    #include <cstdio> 
    #include <cstdlib> 
    #include <cstring> 
    #include <cmath> 
    #include <algorithm> 
    #include <queue>
    #include <vector> 
    using namespace std;
    const int MAX_N = 2e5 + 5; 
    char s[MAX_N]; 
    int N, tot, nd[MAX_N], ans[MAX_N]; 
    struct Trie { int ch[26], cpy[26], fail, fa, End; } t[MAX_N]; 
    void build() {
        static queue<int> que; 
        for (int i = 0; i < 26; i++) if (t[0].ch[i]) que.push(t[0].ch[i]), t[t[0].ch[i]].fail = 0; 
        while (!que.empty()) { 
            int o = que.front(); que.pop(); 
            for (int i = 0; i < 26; i++) {
                if (t[o].ch[i]) t[t[o].ch[i]].fail = t[t[o].fail].ch[i], que.push(t[o].ch[i]);
                else t[o].ch[i] = t[t[o].fail].ch[i]; 
            } 
        } 
    } 
    struct Graph { int to, next; } e[MAX_N << 1]; int fir[MAX_N], e_cnt = 0; 
    void clearGraph() { memset(fir, -1, sizeof(fir)); e_cnt = 0; } 
    void Add_Edge(int u, int v) { e[e_cnt] = (Graph){v, fir[u]}, fir[u] = e_cnt++; } 
    struct Query { int x, id; } ; 
    vector<Query> vec[MAX_N]; 
    int tim, L[MAX_N], R[MAX_N]; 
    void dfs(int x) {
        L[x] = ++tim; 
        for (int i = fir[x]; ~i; i = e[i].next) dfs(e[i].to); 
        R[x] = tim; 
    }
    int c[MAX_N]; 
    inline int lb(int x) { return x & -x; } 
    void add(int x, int v) { while (x <= tim) c[x] += v, x += lb(x); } 
    int sum(int x) { int res = 0; while (x > 0) res += c[x], x -= lb(x); return res; } 
    void DFS(int x) { 
        add(L[x], 1); 
        if (t[x].End) 
            for (vector<Query> :: iterator ite = vec[t[x].End].begin(); ite != vec[t[x].End].end(); ++ite) 
                ans[ite->id] = sum(R[nd[ite->x]]) - sum(L[nd[ite->x]] - 1);
        for (int i = 0; i < 26; i++) 
            if (t[x].cpy[i]) DFS(t[x].cpy[i]); 
        add(L[x], -1); 
    } 
    int main () {
        scanf("%s", s + 1);
        for (int o = 0, i = 1, l = strlen(s + 1); i <= l; i++) { 
            if ('a' <= s[i] && s[i] <= 'z') {
                if (!t[o].ch[s[i] - 'a']) t[o].ch[s[i] - 'a'] = ++tot, t[tot].fa = o; 
                o = t[o].ch[s[i] - 'a']; 
            } 
            if (s[i] == 'B') o = t[o].fa; 
            if (s[i] == 'P') nd[++N] = o, t[o].End = N; 
        } 
        for (int i = 0; i <= tot; i++) 
            for (int o = 0; o < 26; o++) t[i].cpy[o] = t[i].ch[o]; 
        build(); clearGraph(); 
        for (int i = 1; i <= tot; i++) Add_Edge(t[i].fail, i); 
        dfs(0); 
        int M; scanf("%d", &M); 
        for (int i = 1; i <= M; i++) {
            int x, y; scanf("%d%d", &x, &y); 
            vec[y].push_back((Query){x, i}); 
        } 
        DFS(0); 
        for (int i = 1; i <= M; i++) printf("%d
    ", ans[i]); 
        return 0; 
    } 
    
  • 相关阅读:
    flume sink两种类型 file_rool 自定义sing com.mycomm.MySink even if there is only one event, the event has to be sent in an array
    为什么引入进程20年后,又引入线程?
    As of Flume 1.4.0, Avro is the default RPC protocol.
    Google Protocol Buffer 的使用和原理
    Log4j 2
    统一日志 统一订单
    网站行为跟踪 Website Activity Tracking Log Aggregation 日志聚合 In comparison to log-centric systems like Scribe or Flume
    Percolator
    友盟吴磊:移动大数据平台的架构、实践与数据增值
    Twitter的RPC框架Finagle简介
  • 原文地址:https://www.cnblogs.com/heyujun/p/10242941.html
Copyright © 2011-2022 走看看