[JSOI2015] 字符串树

题目传送门

  • 给出 \(n\) 个点的树,边上有字符串 \(s\)\(q\) 次询问 \(u,v\) 两点路径上有多少边的字符串以字符串 \(t\) 为前缀。

  • \(n,q\le 10^5\)\(|s_i|\le 10\)

注意到 \(|s_i|\le 10\),即本质不同的前缀不会超过 \(10^6\) 种。于是先边转点,然后运用 P5838 的套路,有两种方法:

  • 【方法一】

    对每种字符串建立动态开点线段树,线段树上的区间维护对应的 \(dfn\) 序区间内该前缀的出现次数。跳链时在对应前缀的线段树上查询。

    时间复杂度为 \(\mathcal{O}(q\log ^2 n)\),空间复杂度为 \(\mathcal{O}\left(\left(\sum\limits_{i=1}^ns_i\right)\times \log n\right)\)

    可以参考我 P5838 的题解。

  • 【方法二】

    对每种前缀开一个 vector,按从小到大的顺序存放具有该前缀节点的 \(dfn\) 序。跳链时,设当前跳到点 \(u\),二分出 \(l\) 表示第一个大于等于 \(dfn_{top_u}\) 的值的位置,\(r\) 表示最后一个不超过 \(dfn_u\) 的位置,则该重链的贡献为 \(r-l+1\)

    时间复杂度为 \(\mathcal{O}(q\log^2 n)\),空间复杂度为 \(\mathcal{O}\left(\sum\limits_{i=1}^ns_i\right)\)

    可以参考我 CF463E 的题解。

相比之下,【方法一】空间更劣,常数更大,但是可以支持更多的修改操作。【方法二】空间较优,常数较小,但是在修改方面有一定的局限性。

给的是【方法二】的代码。

评测记录

#include <bits/stdc++.h>
#define str string 
#define vec vector
#define P pair
#define eb emplace_back
#define fi first 
#define se second
#define bg begin 
#define ed end
using namespace std; const int N = 1e5 + 5;
int n, q, dep[N], top[N], siz[N], fa[N], dfn[N], id, hson[N]; 
map<str, vec<int>> mp; str s, t; vec<P<int, str>> g[N]; str col[N];
void dfs1(int u) {
    siz[u] = 1;
    for (auto i : g[u]) {
        if (i.fi == fa[u]) continue;
        col[i.fi] = i.se, dep[i.fi] = dep[fa[i.fi] = u] + 1;
        dfs1(i.fi), siz[u] += siz[i.fi];
    }
}
void dfs2(int u) {
    for (auto i : g[u])
        if (i.fi != fa[u]) {
            if ((siz[i.fi] << 1) > siz[u]) top[hson[u] = i.fi] = top[u];
            else top[i.fi] = i.fi; 
            dfs2(i.fi);
        }
}
void dfs3(int u) {
    dfn[u] = ++id, t = ""; int l = col[u].size();
    for (int i = 0; i < l; ++i) t += col[u][i], mp[t].eb(id);
    if (hson[u]) dfs3(hson[u]);
    for (auto i : g[u]) {
        if (i.fi == fa[u] || i.fi == hson[u]) continue; dfs3(i.fi);
    }
}
int query(int x, int y, str k) {
    if (mp.find(k) == mp.end()) return 0; int ret = 0;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        int u = lower_bound(mp[k].bg(), mp[k].ed(), dfn[top[x]]) - mp[k].bg();
        int v = upper_bound(mp[k].bg(), mp[k].ed(), dfn[x]) - mp[k].bg() - 1;
        ret += v - u + 1; x = fa[top[x]];
    }
    if (x == y) return ret;//有个 shaber 忘记处理同一重链的情况了,我不说是谁。
    if (dep[x] > dep[y]) swap(x, y);
    int u = lower_bound(mp[k].bg(), mp[k].ed(), dfn[x] + 1) - mp[k].bg();
    int v = upper_bound(mp[k].bg(), mp[k].ed(), dfn[y]) - mp[k].bg() - 1;
    return ret + v - u + 1;
}
signed main() {
    cin.tie(0), cout.tie(0), ios::sync_with_stdio(0); cin >> n;
    for (int i = 1, u, v; i < n; ++i)
        cin >> u >> v >> s, g[u].eb(v, s), g[v].eb(u, s);
    dfs1(1), dfs2(top[1] = 1), dfs3(1); cin >> q;
    for (int u, v; q--;) cin >> u >> v >> s, cout << query(u, v, s) << '\n';
    return 0;
}
posted @ 2023-09-15 09:19  lzyqwq  阅读(16)  评论(0)    收藏  举报