CF587F Duff is Mad [AC自动机,离线根号分治,树状数组]

题意:

here
\(s_{[l,r]}\)\(s_k\) 的出现次数

很妙的一道题。

我们考虑到 \(AC\) 自动机的本质。

\(k\) 的子树里都是包含 \(s_k\) 的串。
\(k\) 的子树查询就相当于查询 \(s_k\) 出现了几次。

然后我们发现。
这个显然可以根号分治,大于 \(\sqrt {\sum len_i}\) 的和小于 \(\sqrt {\sum len_i}\) 的分别处理。

如果暴力做,是把 \(s_k\) 每个点设置成 \(1\),然后 \(l\) ~ \(r\) 都查询一遍子树和。
这样复杂度很高,但是可以拿来用。
我们对于 \(|s_k| \geq \sqrt {\sum len_i}\) 的,就把 \(l\) ~ \(r\) 记录一遍前缀和,相减就是。

我们考虑一下 \(s_k \leq \sqrt {\sum len_i}\) 的。

我们如何改变子树查询呢?

我们查询 \(l\) ~ \(r\) 都是查询子树对吧。

然后我们发现,你查询子树,其实无异于增加子树然后把 \(s_k\) 的贡献加进去。
简单来说呢,就是你可以计算出来每个点的覆盖次数,然后加进答案就好了。

然后这题没了。

#include <bits/stdc++.h>

int read() {
  int x = 0;
  char c = getchar();
  while (c < 48) c = getchar();
  while (c > 47) x = x * 10 + (c - 48), c = getchar();
  return x;
}

using namespace std;
#define int long long

const int maxn = 1e5 + 51;
char s[maxn];

int n, q, S;
vector<pair<int, int>> L[maxn], R[maxn], le[maxn], ri[maxn];
int len[maxn], tot, ans[maxn];

struct BIT {
  int c[maxn];

  BIT() { memset(c, 0, sizeof(c)); }

  int low(int x) { return x & -x; }

  void add(int x, int y) {
    for (; x < maxn; x += low(x)) c[x] += y;
  }

  int qry(int x) {
    int ans = 0;
    for (; x; x ^= low(x)) ans += c[x];
    return ans;
  }
} bit;

struct ACAM {
  int ch[maxn][26], cnt, ed[maxn], fail[maxn], fa[maxn] ;
	
  ACAM() { cnt = 1; }
  
	vector<int> g[maxn];
	
  void ins(const int& len, const int& id) {
    int p = 1;
    for (int i = 1; i <= len; i++) {
      int c = s[i] - 'a';
      if (!ch[p][c]) ch[p][c] = ++cnt, fa[cnt] = p;
      p = ch[p][c];
    }
    ed[id] = p;
  }

  void build() {
    queue<int> q;
    for (int i = 0; i < 26; i++)
      if (ch[1][i])
        fail[ch[1][i]] = 1, q.push(ch[1][i]);
      else
        ch[1][i] = 1;
    while (q.size()) {
      int u = q.front();
      q.pop();
      for (int i = 0; i < 26; i++) {
        if (ch[u][i]) {
          fail[ch[u][i]] = ch[fail[u]][i];
          q.push(ch[u][i]);
        } else {
          ch[u][i] = ch[fail[u]][i];
        }
      }
    }

    for (int i = 2; i <= cnt; i++) g[fail[i]].push_back(i);
  }

	
  int sz[maxn], dfn[maxn], idx = 0;
  
  void dfs1(int u) {
    for (int v : g[u]) dfs1(v), sz[u] += sz[v];
  }

  void dfs2(int u) {
    sz[u] = 1, dfn[u] = ++idx;
    for (int v : g[u]) dfs2(v), sz[u] += sz[v];
  }

  void solve() {
    for (int i = 1; i <= n; i++) {
      if (len[i] > S) {
        int p = ed[i];
        memset(sz, 0, sizeof(sz));
        while (p ^ 1) {
          sz[p] = 1, p = fa[p];
        }
        dfs1(1);
        sort(L[i].begin(), L[i].end()), sort(R[i].begin(), R[i].end());
        reverse(L[i].begin(), L[i].end()), reverse(R[i].begin(), R[i].end());
        int qwq = 0;
        for (int j = 1; j <= n; j++) {
          while (L[i].size() && L[i].back().first == j) ans[L[i].back().second] -= qwq, L[i].pop_back();
          qwq += sz[ed[j]];
          while (R[i].size() && R[i].back().first == j) ans[R[i].back().second] += qwq, R[i].pop_back();
        }
      }
    }
    dfs2(1);
    for (int i = 1; i <= n; i++) {
      for (auto x : le[i]) {
        int p = ed[x.first];
        while (p ^ 1) {
          ans[x.second] -= bit.qry(dfn[p]);
          p = fa[p];
        }
      }
      bit.add(dfn[ed[i]], 1);
      bit.add(dfn[ed[i]] + sz[ed[i]], -1);
      for (auto x : ri[i]) {
        int p = ed[x.first];
        while (p ^ 1) {
          ans[x.second] += bit.qry(dfn[p]);
          p = fa[p];
        }
      }
    }
  }
  
} acam;

signed main() {
  n = read(), q = read();
  for (int i = 1; i <= n; i++) {
    scanf("%s", s + 1), len[i] = strlen(s + 1);
    acam.ins(len[i], i);
    tot += len[i];
  }
  acam.build(), S = sqrt(tot);
  for (int i = 1; i <= q; i++) {
    int l = read(), r = read(), k = read();
    if (len[k] > S) {
      L[k].push_back({ l, i });
      R[k].push_back({ r, i });
    } else {
      le[l].push_back({ k, i });
      ri[r].push_back({ k, i });
    }
  }
  acam.solve();
  for (int i = 1; i <= q; i++) printf("%lld\n", ans[i]);
  return 0;
}
posted @ 2020-03-17 20:38  _Isaunoya  阅读(230)  评论(0编辑  收藏  举报