Codeforces 547E Mike and Friends

蔡队题:AC自动机上fail树dfs序建可持久化线段树

https://codeforces.com/problemset/problem/547/E

题意:给 \(n\) 个串,串总长不超过 \(2e5\)\(q\) 次询问,每次询问 \(l\to r\) 这些串中出现了多少次 \(k\) 串。

题解:

考虑x个串,求k的出现次数,可以对这x个串建AC自动机,每个点记录被这x个串一共访问了多少次,然后把k串放在上面跑所有的逆fail指针,每到一个点就把它的权值加上即是答案。

那么多次询问下,将所有串建立AC自动机,如果我想询问 \(l\to r\) 这些串出现了多少次 \(k\) 串,只需要计算 \(l \to r\) 这些串对答案的总贡献, \(k\) 串就直接在fail树上表达为一个子树。

而在insert一个串时记录它末尾字符在字典树的序号,然后这个点在fail树上的子树和可以直接dfs序维护,问题变成了在一棵树上如何计算子树和、单点加。

还有一个问题没有处理,每次询问的 \(l \to r\) 是不同的,由于答案互不影响,考虑差分 \(ans(r)-ans(l-1)\),那么将询问排序,再扫描线求值即可。

当然这里直接写带历史版本的权值线段树可以做到在线询问,复杂度会多一个log。

代码(树状数组离线询问版本):

/*================================================================
*
*   创 建 者: badcw
*   创建日期: 2020/6/20 1:41
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5 + 50;
const int mod = 1e9 + 7;

ll qp(ll a, ll n, ll mod = ::mod) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

int now[maxn], out[maxn];
int tot;
class AC_automation {
public:
    int trie[maxn][26], cnt;
    int fail[maxn], in[maxn], Map[maxn], fa[maxn];
    void insert(char *str, int id) {
        int root = 0;
        for (int i = 0; str[i]; i++) {
            int id = str[i] - 'a';
            if (!trie[root][id]) trie[root][id] = ++cnt;
            fa[trie[root][id]] = root;
            root = trie[root][id];
        }
        Map[id] = root;
    }
    vector<int> edge[maxn];
    void build() {
        queue<int> que;
        for (int i = 0; i < 26; i++) if (trie[0][i]) {
            edge[0].push_back(trie[0][i]);
//                cerr << "add edge: " << 0 << " " << trie[0][i] << endl;
            que.push(trie[0][i]);
        }
        while (!que.empty()) {
            int k = que.front();
            que.pop();
            for (int i = 0; i < 26; i++) {
                if (trie[k][i]) {
                    fail[trie[k][i]] = trie[fail[k]][i];
                    edge[fail[trie[k][i]]].push_back(trie[k][i]);
//                    cerr << "add edge: " << fail[trie[k][i]] << " " << trie[k][i] << endl;
                    que.push(trie[k][i]);
                    in[fail[trie[k][i]]]++;
                } else trie[k][i] = trie[fail[k]][i];
            }
        }
    }
    void dfs(int u) {
        now[u] = ++tot;
        for (auto v : edge[u]) {
            dfs(v);
        }
        out[u] = tot;
    }
} AC;

struct que {
    int x, k, sgn, pos;
    que(int i, int i1, int i2, int i3):x(i),k(i1),sgn(i2),pos(i3) {}

    bool operator < (const que& oth) const {
        return x < oth.x;
    }
};
int res[500005];
int N;
int pre[maxn];

void add(int pos) {
    while (pos <= N) {
        pre[pos] ++;
        pos += pos & -pos;
    }
}

int query(int pos) {
    int res = 0;
    while (pos > 0) {
        res += pre[pos];
        pos -= pos & -pos;
    }
    return res;
}

char s[maxn];
int main(int argc, char *argv[]) {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++i) {
        scanf("%s", s);
        AC.insert(s, i);
    }
    AC.build();
    AC.dfs(0);
    N = out[0];
    vector<que> ned;
    for (int i = 1; i <= q; ++i) {
        int x, y, k;
        scanf("%d%d%d", &x, &y, &k);
        ned.emplace_back(x - 1, k, -1, i);
        ned.emplace_back(y, k, 1, i);
    }
    sort(ned.begin(), ned.end());
    int pos = 1;
    for (auto i : ned) {
        while (pos <= i.x) {
//            cerr << "***add: " << pos << endl;
            int nowx = AC.Map[pos];
            while (nowx) {
//                cerr << now[nowx] << " ";
                add(now[nowx]), nowx = AC.fa[nowx];
            }
//            cerr << endl;
            pos ++;
        }
        res[i.pos] += i.sgn * (query(out[AC.Map[i.k]]) - query(now[AC.Map[i.k]] - 1));
    }
    for (int i = 1; i <= q; ++i) {
        printf("%d\n", res[i]);
    }
    return 0;
}

主席树版本:

/*================================================================
*   文件名称:E.cpp
*   创 建 者: badcw
*   创建日期: 10/22/19
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

template <class T>
inline bool scan(T& ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; // EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

//template <class T>
//inline void out(T x) {
//    if (x > 9) out(x / 10);
//    putchar(x % 10 + '0');
//}

int root[maxn];
int cnt = 0;
struct node {
    int l, r, val;
}p[maxn * 40];

void update(int l, int r, int pre, int &now, int pos) {
    if (pre == now) {
        p[now=++cnt] = p[pre];
    }
    p[now].val ++;
    if (l == r) return;
    int mid = l + r >> 1;
    if (pos <= mid) update(l, mid, p[pre].l, p[now].l, pos);
    else update(mid + 1, r, p[pre].r, p[now].r, pos);
}

int le, re;

inline int query(int l, int r, int x, int y) {
    if (p[y].val == p[x].val) return 0;
    if (le <= l && r <= re) {
        return p[y].val - p[x].val;
    }
    int mid = l + r >> 1;
    int res = 0;
    if (le <= mid) res += query(l, mid, p[x].l, p[y].l);
    if (re > mid) res += query(mid + 1, r, p[x].r, p[y].r);
    return res;
}

int n;
string s[maxn];
vector<int> edge[maxn];
int pos[maxn];
int rnk[maxn], ot[maxn];
int tot;

class AC_automation
{
public:
    int trie[maxn][26];
    int tag[maxn];
    int fail[maxn], num[maxn], res[maxn], in[maxn];

    void init() {
        memset(trie, 0, sizeof trie);
        memset(tag, 0, sizeof tag);
        memset(fail, 0, sizeof fail);
    }

    int insert(string str) {
        int root = 0;
        for (int i = 0; i < str.length(); i++)
        {
            int id = str[i] - 'a';
            if (!trie[root][id]) {
                trie[root][id] = ++n;
            }
            root = trie[root][id];
        }
        return root;
    }

    void build() {
        queue<int> que;
        for (int i = 0; i < 26; i++) if (trie[0][i]) que.push(trie[0][i]);
        while (!que.empty())
        {
            int k = que.front();
            que.pop();
            for (int i = 0; i < 26; i++)
            {
                if (trie[k][i])
                {
                    fail[trie[k][i]] = trie[fail[k]][i];
                    que.push(trie[k][i]);
                    in[fail[trie[k][i]]] ++;
                } else trie[k][i] = trie[fail[k]][i];
            }
        }
    }
} AC;

inline void build(int pre, int &now, int id) {
    int rt = 0;
    now = pre;
    for (char i : s[id]) {
        rt = AC.trie[rt][i - 'a'];
        update(1, n, pre, now, rnk[rt]);
    }
}

inline void dfs(int u) {
    rnk[u] = ++tot;
    for (auto v : edge[u]) {
        dfs(v);
    }
    ot[u] = tot;
}

int N, Q;

int main(int argc, char* argv[]) {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> N >> Q;
    for (int i = 1; i <= N; ++i) {
        cin >> s[i];
        pos[i] = AC.insert(s[i]);
    }
    n ++;
    AC.build();
    for (int i = 1; i <= n; ++i) {
        edge[AC.fail[i]].push_back(i);
    }
    dfs(0);
    for (int i = 1; i <= N; ++i) build(root[i - 1], root[i], i);
    while (Q--) {
        int l, r, k;
        cin >> l >> r >> k;
        le = rnk[pos[k]], re = ot[pos[k]];
        cout << query(1, n, root[l - 1], root[r]) << endl;
    }
    return 0;
}
posted @ 2020-06-20 02:41  badcw  阅读(159)  评论(0编辑  收藏  举报