• [SCOI2012] 喵星球上的点名


    题目描述

    (N) 只喵,每只喵有一个名和一个姓(两个字符串)。

    还有 (M) 次点名(也是一个字符串),如果一只喵的名或姓中包含这个字符串,这只喵就会喊“到”。

    有两问 :

    1. 对于每次点名询问有多少只喵喊“到”。

    2. 对于每一只喵问询她喊了多少次“到”。

    字符集 (|Sigma| le 10^4), 总字符串长不超过 (2 imes 10^5)

    正解

    简单分析

    先可以把一只喵的名和姓合并在一起,中间插入一个不存在的字符,这样就不需要考虑两个串了。

    询问是类似于字符串 (x) 在字符串 (y) 中是否出现过。

    如果字符串出现多次算多次的话,这里有一道用 AC 自动机 经典的例题

    考虑 AC 自动机 ( ext{fail}) 树的性质,( ext{fail}) 指针指向的是最长相同后缀。

    如果字符串 (A) 是字符串 (B) 的后缀, 那么在 AC 自动机上面,从 (B) 开始跳 ( ext{fail}) 树,一定可以跳到 (A)

    也就是说,(B)(A) 的子树内,(A)(B) 的祖先。(在 ( ext{fail}) 树上)

    判断 (A) 是否在 (B) 中出现过,就可以对于 (B) 的每一个前缀(子串一定是一个前缀的后缀),在 ( ext{fail}) 树上暴力往上跳进行修改或者查询即可。

    但是暴力跳 ( ext{fail}) 复杂度可能不太对,但是好像也可以通过此题,这里给出一个复杂度为 (O(n log n)) 的做法。

    第一问

    对于一个名字串的每一个前缀(总前缀个数不超过字符串总长),覆盖它到根的路径(覆盖表示加多次算一次)。

    对每一个名字串都这么做,看点名串总共被多少个名字串给覆盖。

    树上链修改,单点查询的问题先转化成树上单点修改,子树查询的问题j。

    由于覆盖多次算只算一次,就要把覆盖多的部分减掉。

    这里有一个小 trick。

    对名字串的前缀按 ( ext{dfs}) 序排序,减掉的部分就是每相邻节点的 ( ext{lca})

    这样就可以做覆盖多次算一次了。

    第二问

    对于一个名字串的所有一个前缀,看它们总共覆盖了多少点名串。

    树上单点修改,链查询的问题先转化串树上子树修改, 单点查询的问题。

    同样利用上面的 trick,减掉 ( ext{dfs}) 序相邻节点 ( ext{lca}) 的贡献即可。

    复杂度分析

    询问都只需要用到排序和树状数组,这一部分复杂度为 (O(n log n))

    但是有一部分的复杂度很迷,就是 ( ext{trie}) 树求 ( ext{fail}) 的那一部分,求 ( ext{fail}) 是暴力跳的(但好像复杂度均摊?),复杂度我也不敢下定论。

    求哪位大佬帮忙证明一下复杂度,或者直接 hack 掉这种做法。

    update :

    字符集比较大的时候确实这样写确实不对的,无论是普通写法(将不存在的 ( ext{to}) 指针设为 ( ext{fail})( ext{to}) 指针),还是临时跳 ( ext{fail}) 并且记忆化,最坏复杂度是 (O(|Sigma| imes N)) 的,但是第二种写法可能远远达不到这个最坏复杂度,而且貌似最坏也就 (10^9) ?,所以可以通过此题。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 5e4 + 5;
    const int M = 1e5 + 5;
    const int S = (N + M) << 1;
    
    int n, m;
    int namePos[N], queryPos[M];
    
    int last, vcnt = 0;
    struct node {
        int fa, fail; // 此处的 fa 为 trie 树上的 fa
        map<int, int> to;
    } a[S];
    
    int que[S];
    int siz[S], dep[S], son[S], fa[S]; // 此处的 fa 相当于 ac 自动机上的 fail
    int dfn[S], top[S], dfc; 
    vector<int> g[S];
    
    namespace BIT {
    #define lowbit(x) (x & -x)
        int c[S];
        void clear() { memset(c, 0, sizeof c); }
        void update(int p, int v) {
            for(int i = p; i <= dfc; i += lowbit(i))
                c[i] += v;
        }
        int sum(int p) {
            int res = 0;
            for(int i = p; i; i -= lowbit(i))
                res += c[i];
            return res;
        }
        int query(int l, int r) { return sum(r) - sum(l - 1); }
    #undef lowbit
    } // namespace BIT
    
    inline int read() {
        int x = 0; char ch = getchar();
        while(!isdigit(ch)) ch = getchar();
        while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
        return x;
    }
    
    void extend(int c) {
        int &v = a[last].to[c];
        if(!v) v = ++vcnt, a[v].fa = last;
        last = v;
    }
    
    int getFail(int u, int c) {
        if(a[u].to.count(c)) return a[u].to[c];
        else if(!u) return u;
        return a[u].to[c] = getFail(a[u].fail, c);
    }
    
    void buildFailTree() {
        int hd = 1, tl = 0;
        for(auto pr : a[0].to)
            que[++tl] = pr.second;
        while(hd <= tl) {
            int u = que[hd++];
            for(auto pr : a[u].to) {
                a[pr.second].fail = getFail(a[u].fail, pr.first);
                que[++tl] = pr.second;
            }
        }
        for(int i = 1; i <= vcnt; ++i)
            g[a[i].fail].push_back(i);
    }
    
    void preDfs(int u) {
        siz[u] = 1;
        dfn[u] = ++dfc;
        dep[u] = dep[fa[u]] + 1;
        for(int v : g[u]) if(v != fa[u]) {
            fa[v] = u, preDfs(v);
            siz[u] += siz[v];
            if(!son[u] || siz[v] > siz[son[u]])
                son[u] = v;
        }
    }
    void getTop(int u, int tp) {
        top[u] = tp;
        if(son[u]) getTop(son[u], tp);
        for(int v : g[u]) if(v != fa[u] && v != son[u]) {
            getTop(v, v); 
        }
    }
    
    inline int lca(int u, int v) {
        while(top[u] != top[v]) {
            if(dep[top[u]] < dep[top[v]]) swap(u, v);
            u = fa[top[u]];
        }
        return dep[u] < dep[v] ? u : v;
    }
    
    int arr[N], tot;
    bool cmp(const int &u, const int &v) { return dfn[u] < dfn[v]; }
    
    void solve1() {
        BIT::clear();
        for(int i = 1, u; i <= n; ++i) {
            u = namePos[i], tot = 0;
            while(u) {
                arr[++tot] = u;
                BIT::update(dfn[u], 1);
                u = a[u].fa;
            }
            sort(arr + 1, arr + tot + 1, cmp);
            for(int j = 1; j < tot; ++j)
                BIT::update(dfn[lca(arr[j], arr[j + 1])], -1);
        }
        for(int i = 1, u; i <= m; ++i) {
            u = queryPos[i];
            printf("%d
    ", BIT::query(dfn[u], dfn[u] + siz[u] - 1));
        }
    }
    void solve2() {
        BIT::clear();
        for(int i = 1, u; i <= m; ++i) {
            u = queryPos[i];
            BIT::update(dfn[u], 1);
            BIT::update(dfn[u] + siz[u], -1);
        }
        for(int i = 1, u, res; i <= n; ++i) {
            u = namePos[i], tot = 0, res = 0;
            while(u) {
                arr[++tot] = u;
                res += BIT::sum(dfn[u]);
                u = a[u].fa;
            }
            sort(arr + 1, arr + tot + 1, cmp);
            for(int j = 1; j < tot; ++j)
                res -= BIT::sum(dfn[lca(arr[j], arr[j + 1])]);
            printf("%d%c", res, " 
    "[i == n]);
        }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("P2336.in", "r", stdin);
        freopen("P2336.out", "w", stdout);
    #endif
        n = read(), m = read();
        for(int i = 1, l, c; i <= n; ++i) {
            last = 0;
            l = read();
            for(int j = 1; j <= l; ++j) {
                c = read();
                extend(c);
            } extend(-1);
            l = read();
            for(int j = 1; j <= l; ++j) {
                c = read();
                extend(c);
            }
            namePos[i] = last;
        }
        for(int i = 1, l, c; i <= m; ++i) {
            last = 0;
            l = read();
            for(int j = 1; j <= l; ++j) {
                c = read();
                extend(c);
            }
            queryPos[i] = last;
        }
    
        buildFailTree();
        
        preDfs(0);
        getTop(0, 0);
        
        solve1();
        solve2();
        return 0;
    }
    
  • 相关阅读:
    课程教学网站(大三上课设)
    mybatis+servlet
    第一个struct2(Hello_World)
    servlet入门
    mybatis入门项目
    XML
    状态模式的介绍及状态机模型的函数库javascript-state-machine的用法和源码解析
    10个JavaScript难点
    markdown基本语法
    Mac OSX 平台安装 MongoDB
  • 原文地址:https://www.cnblogs.com/Lskkkno1/p/12926265.html
Copyright © 2020-2023  润新知