BZOJ4231 回忆树
1|0BZOJ4231 回忆树
1|1题目分析:
很恶心的题目呢?(可能是我太菜了)
首先我们把树上的所有的询问看成两部分,一部分是经过 lca 的部分,另一部分是链的形式。很明显只有这两种贡献。
那么对于第一种来说,因为总共的字符集大小是不超过 300000 的,所以我们可以暴力找出经过 lca 的这一部分然后进行 KMP 匹配。这一部分的复杂度是 的。
那么对于第二种来说,我们可以看成是一种差分,即从根到点的差分。我们可以对于询问串建一个 AC 自动机,这样的话我们就可以在 dfs 的时候进行统计答案,遍历到当前的节点加上贡献,出去的时候减去贡献,因为是子树内权值和。所以考虑离线 + 树状数组实现。
SB 错误:出去的时候减错点了,导致最后出现了负数(大雾
1|2Code:
//editor : DRYAYST
//Wo shi ge da SHA BI
#include<bits/stdc++.h>
#define g() getchar()
#define il inline
#define ull unsigned long long
#define eps 1e-10
#define ll long long
#define pa pair<int, int>
#define for_1(i, n) for(int i = 1; i <= (n); ++i)
#define for_0(i, n) for(int i = 0; i < (n); ++i)
#define for_xy(i, x, y) for(int i = (x); i <= (y); ++i)
#define for_yx(i, y, x) for(int i = (y); i >= (x); --i)
#define for_edge(i, x) for(int i = head[x]; i; i = nxt[i])
//#define int long long
#define DB double
#define ls (p<<1)
#define rs (p<<1|1)
#define m_p make_pair
#define fi first
#define se second
using namespace std;
const int N = 2e5 + 10, INF = 0x7f7f7f7f, mod = 1e9 + 7;
inline int re() {
int x = 0, p = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') p = -1; ch = getchar();}
while(ch <= '9' and ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * p;
}
int head[N], ver[N<<1], nxt[N<<1], tot, edge[N<<1];
il void add(int x, int y, int z) {ver[++tot] = y; nxt[tot] = head[x]; head[x] = tot; edge[tot] = z; }
int n, m, cnt, num, fk;
int f[N][18], fval[N], dep[N], tr[N<<1][30], pos[N][2], st[N<<1], ed[N<<1], fail[N<<1], sum[N], net[N], ans[N];
char St[N], s1[N*2];
vector<pa> Ps[N];
vector<int> e[N<<1], ID[N];
il int lowbit(int x) {return x & (-x); }
il void Add(int x, int k) {while(x <= fk) sum[x] += k, x += lowbit(x);}
il int Ask(int x) {int ans = 0; while(x) ans += sum[x], x -= lowbit(x); return ans; }
il int Insert(int tp, char *s) {
int u = 0; int len = strlen(s + 1);
if(tp == -1) {for(int i = len; i >= 1; --i) {int now = s[i] - 'a' + 1; if(!tr[u][now]) tr[u][now] = ++num; u = tr[u][now]; }}
else for(int i = 1; i <= len; ++i) { int now = s[i] - 'a' + 1; if(!tr[u][now]) tr[u][now] = ++num; u = tr[u][now];}
return u;
}
il void Getfail() {
queue<int> q; while(q.size()) q.pop(); for(int i = 1; i <= 26; ++i) if(tr[0][i]) q.push(tr[0][i]);
while(q.size()) {
int now = q.front(); q.pop();
for(int i = 1; i <= 26; ++i) {
if(tr[now][i]) fail[tr[now][i]] = tr[fail[now]][i], q.push(tr[now][i]);
else tr[now][i] = tr[fail[now]][i];
}
}
for(int i = 1; i <= num; ++i) e[fail[i]].push_back(i);
}
void dfs2(int x) {st[x] = ++fk; for(int v:e[x]) {dfs2(v);} ed[x] = fk; }
void dfs1(int x, int fa) {
dep[x] = dep[fa] + 1; f[x][0] = fa; for_1(i, 17) f[x][i] = f[f[x][i-1]][i-1];
for_edge(i, x){ int y = ver[i], z = edge[i]; if(y == fa) continue; fval[y] = z; dfs1(y, x); }
}
il int lca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y); for(int i = 17; i >= 0; --i) if(dep[f[x][i]] >= dep[y]) x = f[x][i]; if(x == y) return x;
for(int i = 17; i >= 0; --i) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; return f[x][0];
}
il int Bz(int x, int k) {for(int i = 17; i >= 0; --i) if((k>>i)&1) x = f[x][i]; return x; }
il void KMP(int x, int y, int lc, int id, char *s) {
int len = strlen(s + 1); int u = Bz(x, dep[x] - min(dep[x], dep[lc] + len - 1)), v = Bz(y, dep[y] - min(dep[y], dep[lc] + len - 1)); cnt = 0;
int l = dep[u] + dep[v] - 2 * dep[lc]; int fu = u; while(fu != lc) s1[++cnt] = fval[fu] + 'a' - 1, fu = f[fu][0]; int tmp = 1, fv = v; while(fv != lc) s1[l - tmp + 1] = fval[fv] + 'a' - 1, fv = f[fv][0], ++tmp;
net[0] = 0; net[1] = net[2] = 0; int k = 0, res = 0;
for(int i = 2; i <= len; ++i) {
while(k and s[k + 1] != s[i]) k = net[k];
if(s[k + 1] == s[i]) ++k; net[i] = k;
} k = 0;
for(int i = 1; i <= l; ++i) {
while(k and s[k + 1] != s1[i]) k = net[k];
if(s[k + 1] == s1[i]) ++k; if(k >= len) ++res;
}
ans[id] += res; pos[id][0] = Insert(1, s); pos[id][1] = Insert(-1, s);
if(x != u) {ID[x].push_back(id); ID[u].push_back(id); Ps[x].push_back(m_p(1, pos[id][1])); Ps[u].push_back(m_p(-1, pos[id][1])); }
if(y != v) {ID[y].push_back(id); ID[v].push_back(id); Ps[y].push_back(m_p(1, pos[id][0])); Ps[v].push_back(m_p(-1, pos[id][0])); }
}
void dfs3(int x, int fa, int u) {
Add(st[u], 1);
for(int i = 0; i < Ps[x].size(); ++i) {
int now = Ask(ed[Ps[x][i].second]) - Ask(st[Ps[x][i].second] - 1);
ans[ID[x][i]] += Ps[x][i].first * now;
}
for_edge(i, x) { int y = ver[i], z = edge[i]; if(y == fa) continue; dfs3(y, x, tr[u][z]); }
Add(st[u], -1);
}
signed main() {
// freopen("memory.in","r",stdin); freopen("memory.out","w",stdout);
n = re(), m = re(); for_1(i, n-1) {int x = re(), y = re(); char op; scanf("%s", &op); add(x, y, op - 'a' + 1); add(y, x, op - 'a' + 1); } dfs1(1, 1);
for_1(i, m) { int x = re(), y = re(); scanf("%s", St + 1); if(x == y) continue; int lc = lca(x, y); KMP(x, y, lc, i, St);} Getfail(); dfs2(0);
dfs3(1, 1, 0); for(int i = 1; i <= m; ++i) cout<<ans[i]<<endl;
}
/*
12 3
1 2 w
2 3 w
3 4 x
4 5 w
5 6 w
6 7 x
7 8 w
8 9 w
9 10 x
10 11 w
11 12 w
1 7
wwx
1 12
www
1 12
w
*/
__EOF__

本文作者:Zwaire
本文链接:https://www.cnblogs.com/Zwaire/p/16137691.html
关于博主:这个世界除了你,都知道我喜欢你
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/Zwaire/p/16137691.html
关于博主:这个世界除了你,都知道我喜欢你
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析