[BZOJ3926][Zjoi2015]诸神眷顾的幻想乡(广义SAM)

如果任意选一个点为根,那么对于一个点 u u ,不停地往下走,总会走到一个度数为1的点。
再注意到题目条件:度数为 1 1 的点不超过20个。
这就意味着可以分别以每个度数为 1 1 的点为根,在Trie树上构建后缀自动机。
在Trie树上构建后缀自动机,加入点u时,需要把 last l a s t 设为 u u 的父亲对应的节点,其他操作与一般SAM无异。
全部构建后,本质不同的子串个数就是u{MaxluMaxlParentu}
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 1e5 + 5, M = 2e5 + 5, L = 4e6 + 5;
struct cyx {
    int go[10], maxl, fa;
    void init() {memset(go, 0, sizeof(go)); maxl = fa = 0;}
} T[L];
int n, c, col[N], ecnt, nxt[M], adj[N], go[M], cnt[N], QAQ;
void add_edge(int u, int v) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; cnt[go[ecnt] = v]++;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; cnt[go[ecnt] = u]++;
}
int ins(int c, int lst) {
    int i = lst; T[lst = ++QAQ].init(); T[lst].maxl = T[i].maxl + 1;
    for (; i && !T[i].go[c]; i = T[i].fa) T[i].go[c] = lst;
    if (!i) T[lst].fa = 1; else {
        int j = T[i].go[c]; if (T[i].maxl + 1 == T[j].maxl)
            T[lst].fa = j; else {
            int p; T[p = ++QAQ] = T[j]; T[lst].fa = T[j].fa = p;
            T[p].maxl = T[i].maxl + 1; for (; i && T[i].go[c] == j;
                i = T[i].fa) T[i].go[c] = p;
        }
    }
    return lst;
}
void dfs(int u, int fu, int lst) {
    int x = ins(col[u], lst); for (int e = adj[u], v; e; e = nxt[e])
        if ((v = go[e]) != fu) dfs(v, u, x);
}
int main() {
    int i, x, y; n = read(); c = read(); T[QAQ = 1].init();
    for (i = 1; i <= n; i++) col[i] = read();
    for (i = 1; i < n; i++) x = read(), y = read(), add_edge(x, y);
    for (i = 1; i <= n; i++) if (cnt[i] == 1) dfs(i, 0, 1); ll ans = 0;
    for (i = 2; i <= QAQ; i++) ans += T[i].maxl - T[T[i].fa].maxl;
    cout << ans << endl; return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值