BZOJ4231 回忆树

BZOJ4231 回忆树

Link

题目分析:

很恶心的题目呢?(可能是我太菜了

首先我们把树上的所有的询问看成两部分,一部分是经过 lca 的部分,另一部分是链的形式。很明显只有这两种贡献。

那么对于第一种来说,因为总共的字符集大小是不超过 300000 的,所以我们可以暴力找出经过 lca 的这一部分然后进行 KMP 匹配。这一部分的复杂度是 \(O(2|S|)\) 的。

那么对于第二种来说,我们可以看成是一种差分,即从根到点的差分。我们可以对于询问串建一个 AC 自动机,这样的话我们就可以在 dfs 的时候进行统计答案,遍历到当前的节点加上贡献,出去的时候减去贡献,因为是子树内权值和。所以考虑离线 + 树状数组实现。

SB 错误:出去的时候减错点了,导致最后出现了负数(大雾

Code:

//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
*/

posted @ 2022-04-12 21:45  Zwaire  阅读(102)  评论(1)    收藏  举报