CF917E Upside Down

方便起见,之后的分析中认为 \(n,m,q,\sum|s_i|\) 同阶。

点分治,对于一个询问 \((i,j,k)\),我们在第一次劈开的位置 \(rt\) 计算。那么路径 \(i\to j\) 会被拆成 \(i\to rt\to j\)。同时,贡献也被拆成三种:

  • \(s_k\)\(i\to rt\) 上。

  • \(s_k\)\(rt\to j\) 上。

  • \(s_k\) 跨越两条链。

先考虑 \(s_k\)\(i\to rt\) 上的情况。

此时考虑对模式串的反串建立 AC 自动机,然后去 dfs 子树,同时去维护当前的文本串。那么我们只需要支持在文本串的末尾进行插入或删除,查询 \(R(s_k)\) 在文本串中出现次数。这里 \(R(S)\) 就是 \(S\) 的反串。对应到 fail 树上是单点改,查询子树和,拍到 dfs 序上就是单点改,查询区间和,容易维护。

\(s_k\)\(rt\to j\) 上的情况类似,我们直接对于模式串建立 AC 自动机即可。

现在我们只要考虑 \(s_k\) 跨越两条链的情况。

考虑对于每个 \(k\),去统一计算查询串为 \(s_k\) 的询问。这里如果 \(k\) 没有对应的询问或者所有询问的路径长度都 \(<|s_k|\) 就跳过。显然 \(s_k\)\(i\to rt\) 中是一段前缀 \(pre\)\(rt\to j\) 中是一段后缀 \(suf\)

那么考虑去枚举 \(pre\),那么这一对 \((pre,suf)\) 对一个查询 \((i,j)\) 有贡献需要如下条件:

  • \(pre\)\(i\to rt\) 串的一个后缀。

  • \(suf\)\(rt\to j\) 串的一个前缀。

考虑建立一个 trie,上面存储所有 \(rt\to u\) 组成的串。同时我们将第一条限制改写成:\(R(pre)\)\(rt\to i\) 串的一个前缀。

也就是说,此时 \(rt\to i\) 串在 trie 树上对应点在 \(R(pre)\) 对应点的子树中,同理,\(rt\to j\) 的对应点在 \(suf\) 对应点的子树中。这里如果 \(R(pre)\) 或者 \(suf\) 在 trie 中没有点,直接忽略这一对 \((pre,suf)\) 即可。

放到 trie 的 dfs 序上考虑,那么 \(rt\to i\) 串和 \(rt\to j\) 串的对应点的限制是一段区间,二维数点即可。此处二维数点需要用 \(O(1)\) 单点修改 \(O(\sqrt n)\) 查询区间和的分块平衡。

至于如何建立 trie 和记录 \(rt\to i\) 串的对应点,可以考虑去 dfs 整棵子树,在遍历到 \(x\) 时记录 \(rt\to x\) 串在 trie 树上的对应点 \(p\),每次加入一个字符 \(c\) 就在 trie 上同时移动 \(p\),如果不存在就创建一个新点。

而求 \(R(pre)\)\(suf\) 的对应点也是好办的,对于 trie 上的每个点 \(x\),我们记录 \(rt\to x\) 串的哈希值,然后用哈希表存哈希值到 trie 上的点的映射即可。

于是这个题就做完了,但是它的复杂度不显然,这里分析一下。

这里唯一复杂度不明确的地方就是 \(s_k\) 跨越两条链的时候,我们对于每个 \(k\),枚举了 \(s_k\) 的前后缀。

考虑对于长度 \(>\sqrt n\) 的模式串 \(s_k\),因为我们排除了路径长度 \(<|s_k|\) 的询问,所以我们只可能在当前子树大小 \(\ge |s_k|\) 的时候去枚举 \(s_k\) 的前后缀。由于点分治每层大小至少除以 \(2\),所以考虑 \(s_k\) 的次数也是 \(O(\sqrt n)\) 的。于是此处复杂度就是 \(O(n\sqrt n)\)

对于长度 \(\le \sqrt n\)\(s_k\),每次枚举 \(s_k\) 的代价是 \(O(\sqrt n)\) 的。而我们只有 \(q\) 次查询,所以总复杂度也不超过 \(O(n\sqrt n)\)

因此总复杂度 \(O(n\sqrt n)\),二维数点的排序可能会多 \(\log\),但是应该不难去掉。代码里懒了,直接用了 stable_sort

#include <bits/stdc++.h>
#define ALL(x) begin(x), end(x)
using namespace std;
void file() {
  freopen("1.in", "r", stdin);
  freopen("1.out", "w", stdout);
}
using ll = long long;

const int kN = 1e5 + 5;

struct ACAM {
  int tot = 0, clo = 0;
  array<int, kN> fail, dfn, siz;
  array<vector<int>, kN> tree;
  array<array<int, 26>, kN> ch;

  int Insert(string str) {
    int p = 0;
    for(char c : str) {
      int &to = ch[p][c - 'a'];
      p = (to ? to : (to = ++tot));
    }
    return p;
  }
  void GetFail() {
    queue<int> q;
    for(int to : ch[0]) {
      if(to) q.push(to);
    }
    for(int x; q.size(); ) {
      x = q.front(), q.pop();
      for(int i = 0; i < 26; i++) {
        int &to = ch[x][i], v = ch[fail[x]][i];
        to ? (q.push(to), fail[to] = v) : (to = v);
      }
    }
    for(int i = 1; i <= tot; i++) {
      tree[fail[i]].push_back(i);
    }
  }
  void Dfs(int x) {
    dfn[x] = ++clo, siz[x] = 1;
    for(int to : tree[x]) {
      Dfs(to), siz[x] += siz[to];
    }
  }
}acps, acrv;

namespace DS {
  const int kB = 385;
  array<int, kB> sum;
  array<int, kN> a;

  int Bel(int x) { return x / kB; }
  int Bl(int b) { return b * kB; }
  int Br(int b) { return (b + 1) * kB - 1; }
  void Update(int x, int v) { a[x] += v, sum[Bel(x)] += v; }
  int Brute(int l, int r) {
    return accumulate(a.data() + l, a.data() + r + 1, 0);
  }
  int Query(int l, int r) {
    int bl = Bel(l), br = Bel(r);
    if(bl == br) return Brute(l, r);
    int ans = accumulate(sum.data() + bl + 1, sum.data() + br, 0);
    return ans + Brute(l, Br(bl)) + Brute(Bl(br), r);
  }
}

int n, m, q;
array<vector<pair<int, char>>, kN> g;
array<string, kN> str;
array<int, kN> pps, prv;
array<tuple<int, int, int>, kN> qry;
array<vector<int>, kN> vec;

array<int, kN> siz;
array<bool, kN> ban;

void Dfs(int x, int fa) {
  siz[x] = 1;
  for(auto k : g[x]) {
    int to = k.first;
    if((to != fa) && !ban[to]) {
      Dfs(to, x), siz[x] += siz[to];
    }
  }
}
int Root(int x, int fa, int all) {
  int maxs = all - siz[x];
  for(auto k : g[x]) {
    int to = k.first;
    if((to != fa) && !ban[to]) maxs = max(maxs, siz[to]);
  }
  if(maxs * 2 <= all) return x;
  for(auto k : g[x]) {
    int to = k.first;
    if((to != fa) && !ban[to]) {
      int tmp = Root(to, x, all);
      if(tmp) return tmp;
    }
  }
  return 0;
}

int qc, rqc;
array<int, kN> dep, fr, ans, rq;
array<int, kN * 2> vq;
array<bool, kN> slv;

void Dfs2(int x, int fa) {
  if(fa) fr[x] = (fr[fa] ? fr[fa] : x);
  for(int qid : vec[x]) {
    if(ans[qid] < 0) vq[++qc] = qid;
  }
  for(auto k : g[x]) {
    int to = k.first;
    if((to != fa) && !ban[to]) {
      dep[to] = dep[x] + 1, Dfs2(to, x);
    }
  }
}

vector<int> vs;
array<vector<int>, kN> vecq;

void Find() {
  vq[qc + 1] = -1;
  sort(vq.data() + 1, vq.data() + qc + 1);
  for(int i = 1; i <= qc; ) {
    int x = vq[i], y = vq[i + 1];
    if(x != y) { i++; continue; }
    int s, t, k;
    tie(s, t, k) = qry[x];
    if((dep[s] + dep[t] >= str[k].size()) && (fr[s] != fr[t])) {
      rq[++rqc] = x;
      slv[x] = 1, ans[x] = 0;
      int sk = get<2> (qry[x]);
      vs.push_back(sk);
      vecq[sk].push_back(x);
    }
    i += 2;
  }
  sort(ALL(vs));
  vs.erase(unique(ALL(vs)), end(vs));
}

void Dfs3(int x, int fa, int cur) { // case: x -> rt
  DS::Update(acrv.dfn[cur], 1);
  for(int id : vec[x]) {
    if(slv[id] && (x == get<0> (qry[id]))) {
      int sk = get<2> (qry[id]), p = prv[sk];
      ans[id] += DS::Query(acrv.dfn[p], acrv.dfn[p] + acrv.siz[p] - 1);
    }
  }
  for(auto k : g[x]) {
    int to; char c;
    tie(to, c) = k;
    if((to != fa) && !ban[to]) {
      Dfs3(to, x, acrv.ch[cur][c - 'a']);
    }
  }
  DS::Update(acrv.dfn[cur], -1);
}

void Dfs4(int x, int fa, int cur) { // case: rt -> y
  DS::Update(acps.dfn[cur], 1);
  for(int id : vec[x]) {
    if(slv[id] && (x == get<1> (qry[id]))) {
      int sk = get<2> (qry[id]), p = pps[sk];
      ans[id] += DS::Query(acps.dfn[p], acps.dfn[p] + acps.siz[p] - 1);
    }
  }
  for(auto k : g[x]) {
    int to; char c;
    tie(to, c) = k;
    if((to != fa) && !ban[to]) {
      Dfs4(to, x, acps.ch[cur][c - 'a']);
    }
  }
  DS::Update(acps.dfn[cur], -1);
}

struct Trie {
  int tot = 0, clo = 0;
  array<int, kN> dfn, siz;
  array<array<int, 26>, kN> ch;

  void Clear() {
    for(int i = 0; i <= tot; i++) {
      dfn[i] = siz[i] = 0, ch[i].fill(0);
    }
    tot = clo = 0;
  }
  int Go(int x, char c) {
    int &to = ch[x][c - 'a'];
    return to ? to : (to = ++tot);
  }
  void Dfs(int x) {
    dfn[x] = ++clo, siz[x] = 1;
    for(int to : ch[x]) {
      if(to) Dfs(to), siz[x] += siz[to];
    }
  }
}trie;

const ll base = 19260817;
const ll mod = 100000000000003007ll;
array<int, kN> prep, sufp, pd;
array<ll, kN> pw;
unordered_map<ll, int> ump;

void Dfs5(int x, int fa, int cur, ll hsh) {
  pd[x] = ump[hsh] = cur;
  for(auto k : g[x]) {
    int to; char c;
    tie(to, c) = k;
    if((to != fa) && !ban[to]) {
      Dfs5(to, x, trie.Go(cur, c), ((__int128)pw[dep[x]] * c + hsh) % mod);
    }
  }
}

struct Node {
  int id, x, y, v;
  Node() { }
  Node(int _id, int _x, int _y, int _v) {
    id = _id, x = _x, y = _y, v = _v;
  }
};
bool operator < (const Node &x, const Node &y) {
  return (x.x != y.x) ? (x.x < y.x) : (x.id < y.id);
}
array<Node, kN * 5> node;

void Count(int tot) {
  stable_sort(node.data() + 1, node.data() + tot + 1);
  for(int i = 1; i <= tot; i++) {
    if(node[i].id) {
      ans[node[i].id] += DS::Query(1, node[i].y);
    }else {
      DS::Update(node[i].y, node[i].v);
    }
  }
  for(int i = 1; i <= tot; i++) {
    if(!node[i].id) {
      DS::Update(node[i].y, -node[i].v);
    }
  }
}

void Clear() {
  for(int sk : vs) vecq[sk].clear();
  for(int i = 1; i <= rqc; i++) slv[rq[i]] = 0;
  vs.clear(), qc = rqc = 0;
  trie.Clear();
  ump = unordered_map<ll, int> ();
}

void Divide(int rt) {
  dep[rt] = fr[rt] = 0;
  Dfs(rt, 0), Dfs2(rt, 0), Find();
  Dfs3(rt, 0, 0), Dfs4(rt, 0, 0);
  Dfs5(rt, 0, 0, 0), trie.Dfs(0);
  for(int sk : vs) {
    int len = str[sk].size();
    for(int i = 1; i <= len; i++) prep[i] = sufp[i] = -1;
    ll hsh = 0;
    for(int i = 1; i < len; i++) {
      hsh = ((__int128)hsh * base + str[sk][i - 1]) % mod;
      if(ump.count(hsh)) prep[i] = ump[hsh];
    }
    hsh = 0;
    for(int i = len; i > 1; i--) {
      hsh = ((__int128)hsh * base + str[sk][i - 1]) % mod;
      if(~prep[i - 1] && ump.count(hsh)) sufp[i] = ump[hsh];
    }
    int nc = 0;
    for(int i = 1; i < len; i++) {
      int x = prep[i], y = sufp[i + 1];
      if(~x && ~y) {
        int lx = trie.dfn[x];
        int rx = trie.dfn[x] + trie.siz[x];
        int ly = trie.dfn[y];
        int ry = trie.dfn[y] + trie.siz[y];
        node[++nc] = Node(0, lx, ly, 1);
        node[++nc] = Node(0, lx, ry, -1);
        node[++nc] = Node(0, rx, ly, -1);
        node[++nc] = Node(0, rx, ry, 1);
      }
    }
    for(int qid : vecq[sk]) {
      int s, t, k;
      tie(s, t, k) = qry[qid];
      node[++nc] = Node(qid, trie.dfn[pd[s]], trie.dfn[pd[t]], 0);
    }
    Count(nc);
  }
  ban[rt] = 1, Clear();
  for(auto k : g[rt]) {
    int to = k.first;
    if(!ban[to]) Divide(Root(to, rt, siz[to]));
  }
}

int main() {
  // file();
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m >> q;
  for(int i = 1; i < n; i++) {
    int u, v; char w;
    cin >> u >> v >> w;
    g[u].emplace_back(v, w);
    g[v].emplace_back(u, w);
  }
  for(int i = 1; i <= m; i++) {
    cin >> str[i];
    string ps, rv;
    ps = rv = str[i];
    reverse(ALL(rv));
    pps[i] = acps.Insert(ps);
    prv[i] = acrv.Insert(rv);
  }
  acps.GetFail(), acps.Dfs(0);
  acrv.GetFail(), acrv.Dfs(0);
  for(int i = 1; i <= q; i++) {
    int s, t, k;
    cin >> s >> t >> k;
    qry[i] = make_tuple(s, t, k);
    vec[s].push_back(i);
    vec[t].push_back(i);
    ans[i] = -1;
  }
  pw[0] = 1;
  for(int i = 1; i <= n; i++) pw[i] = (__int128)pw[i - 1] * base % mod;
  Dfs(1, 0), Divide(Root(1, 0, n));
  for(int i = 1; i <= q; i++) cout << max(ans[i], 0) << "\n";
  return 0;
}
posted @ 2025-02-19 22:15  CJzdc  阅读(35)  评论(0)    收藏  举报