欢迎光临 ~|

Laijinyi

园龄:1年7个月粉丝:2关注:2

BZOJ 4231 回忆树

以下为自己口胡,未经网上搜索题解验证

Statement

一棵 n 个点的树,每条边有一个小写字母边权,m 次询问,每次给定 u,v,s,问字符串 suv 路径组成的字符串中出现了几次。n,m105,|s|3105.

Solution

Sol 1

uv 路径拆成 ulca,lcav 两条路径,答案等于 ulcas 的出现次数 + lcavs 的出现次数 + s 经过 lca 的出现次数。

s 经过 lca 的出现次数”由于 |s|3105,直接把 lca 两边长度为 |s| 的串拼起来,跑 KMP / ACAM 即可。

问题变成分别求“ulcas 的出现次数”与“lcavs 的出现次数”。第一问可转化为“uroots 的出现次数”,第二问可转化为“rootus 的出现次数”,直接差分一下就是第一二问真正的答案。

uroots 的出现次数”又可以转化为“rootus 的反串的出现次数”,于是问题变成了快速求 rootus 的出现次数。

把所有询问离线到每个 u 上,对于所有 s 建 ACAM,考虑 DFS 一遍求出所有答案。

u 向他的一个儿子 v 走,那么 pos(u) 对应的在 ACAM 上走一步 w(u,v) 得到 pos(v)

可以这样解决:每次在 pos(u) 上单点加,对于询问 (u,si) 就查 ACAM 上 si 子树和就行了,树状数组可以维护,回溯时撤回修改。

Sol 2

树剖,每个重链维护 SAM,预处理 SAM 上每个点的 endpos 集合大小,对于重链直接按 s 在 SAM 上走即可,轻重链切换位置需要暴力匹配。O(mlogn).


不会其他做法了 QAQ

Code

#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
#define reo(i, j, k) for (int i = (j); i >= (k); --i)
typedef long long ll;
const int N = 2e5 + 10, LN = 20, M = 6e5 + 10;
struct Edge {
int t, n;
char c;
} e[N << 1];
int n, m, tot, h[N];
void Add(int u, int v, char c) {
e[++tot] = (Edge){v, h[u], c}, h[u] = tot;
}
int ln, dep[N], Fa[N][LN];
char Faw[N];
void DFS1(int u, int fa) {
dep[u] = dep[fa] + 1, Fa[u][0] = fa;
for (int i = h[u], v; i; i = e[i].n) {
v = e[i].t;
if (v != fa) {
Faw[v] = e[i].c;
DFS1(v, u);
}
}
}
void inittree() {
DFS1(1, 0);
ln = __lg(n);
rep(j, 1, ln) rep(i, 1, n) Fa[i][j] = Fa[Fa[i][j - 1]][j - 1];
}
int LCA(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
reo(i, ln, 0) if (dep[Fa[u][i]] >= dep[v]) u = Fa[u][i];
if (u == v) return u;
reo(i, ln, 0) if (Fa[u][i] != Fa[v][i]) u = Fa[u][i], v = Fa[v][i];
return Fa[u][0];
}
string s[N];
int ans[N];
struct Query {
int id, xishu, sid;
};
vector<Query> Queries[N];
int tim, dfn[M], sz[M];
vector<int> G[M];
void DFS3(int u) {
dfn[u] = ++tim, sz[u] = 1;
for (int v : G[u]) DFS3(v), sz[u] += sz[v];
}
struct ACAM {
int tot, ch[M][26], fail[M], cnt[M], ansid[N];
ACAM() {
tot = 0;
memset(ch, 0, sizeof(ch));
memset(fail, 0, sizeof(fail));
memset(cnt, 0, sizeof(cnt));
memset(ansid, 0, sizeof(ansid));
}
void init() {
reo(p, tot, 0) {
rep(i, 0, 25) ch[p][i] = 0;
fail[p] = 0, cnt[p] = 0;
}
tot = 0, ansid[0] = 0;
}
void ins(string& s, int id = 0) {
int slen = s.length(), p = 0;
rep(i, 0, slen - 1) {
int now = s[i] - 'a';
if (!ch[p][now]) ch[p][now] = ++tot;
p = ch[p][now];
}
++cnt[p], ansid[id] = p;
}
void Buildfail() {
queue<int> q;
rep(i, 0, 25) if (ch[0][i]) q.push(ch[0][i]);
while (!q.empty()) {
int u = q.front();
q.pop();
rep(i, 0, 25)
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];
}
}
int KMP(string& s) {
int slen = s.length(), p = 0, ans = 0;
rep(i, 0, slen - 1)
p = ch[p][s[i] - 'a'], ans += cnt[p];
return ans;
}
void InitFailtree() {
rep(i, 1, tot) G[fail[i]].push_back(i);
DFS3(0);
}
} KMPAM, ACAM;
struct BIT {
int sum[M];
BIT() {
memset(sum, 0, sizeof(sum));
}
void upd(int x, int v) {
for (; x <= ACAM.tot + 1; x += x & -x) sum[x] += v;
}
int qry(int x) {
int res = 0;
for (; x; x -= x & -x) res += sum[x];
return res;
}
int qry(int l, int r) {
return qry(r) - qry(l - 1);
}
} bit;
void DFS2(int u, int fa, int pos = 0) {
bit.upd(dfn[pos], 1);
for (auto qry : Queries[u]) {
int now = ACAM.ansid[qry.sid];
ans[qry.id] += qry.xishu * bit.qry(dfn[now], dfn[now] + sz[now] - 1);
}
for (int i = h[u], v; i; i = e[i].n) {
v = e[i].t;
if (v != fa) {
DFS2(v, u, ACAM.ch[pos][e[i].c - 'a']);
}
}
bit.upd(dfn[pos], -1);
}
void SolveQueries() {
ACAM.Buildfail();
ACAM.InitFailtree();
DFS2(1, 0);
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> n >> m;
rep(i, 1, n - 1) {
int u, v;
char c;
cin >> u >> v >> c, Add(u, v, c), Add(v, u, c);
}
inittree();
rep(i, 1, m) {
int u, v, lca;
cin >> u >> v >> s[i];
s[i + m] = s[i];
reverse(s[i + m].begin(), s[i + m].end());
lca = LCA(u, v);
int len = dep[u] + dep[v] - 2 * dep[lca], slen = s[i].length();
if (len < slen) continue;
ACAM.ins(s[i], i), ACAM.ins(s[i + m], i + m);
if (u == lca || v == lca) {
if (u == lca) {
Queries[v].push_back((Query){i, 1, i});
int p = v;
reo(i, ln, 0) if (dep[Fa[p][i]] >= dep[u] + slen - 1) p = Fa[p][i];
Queries[p].push_back((Query){i, -1, i});
} else {
Queries[u].push_back((Query){i, 1, i + m});
int p = u;
reo(i, ln, 0) if (dep[Fa[p][i]] >= dep[v] + slen - 1) p = Fa[p][i];
Queries[p].push_back((Query){i, -1, i + m});
}
} else {
int p = -1, q = -1;
if (dep[u] - dep[lca] >= slen) {
Queries[u].push_back((Query){i, 1, i + m});
p = u;
reo(i, ln, 0) if (dep[Fa[p][i]] >= dep[lca] + slen - 1) p = Fa[p][i];
Queries[p].push_back((Query){i, -1, i + m});
}
if (dep[v] - dep[lca] >= slen) {
Queries[v].push_back((Query){i, 1, i});
q = v;
reo(i, ln, 0) if (dep[Fa[q][i]] >= dep[lca] + slen - 1) q = Fa[q][i];
Queries[q].push_back((Query){i, -1, i});
}
if (p == -1) p = u;
if (q == -1) q = v;
KMPAM.init();
string t, tt;
while (p != lca) t += Faw[p], p = Fa[p][0];
while (q != lca) tt += Faw[q], q = Fa[q][0];
reverse(tt.begin(), tt.end());
t += tt;
KMPAM.ins(s[i]);
KMPAM.Buildfail();
ans[i] += KMPAM.KMP(t);
}
}
SolveQueries();
rep(i, 1, m) cout << ans[i] << '\n';
return 0;
}

本文作者:Laijinyi

本文链接:https://www.cnblogs.com/laijinyi/p/18403596

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Laijinyi  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起