Codeforces Bubble Cup 11 J. Moonwalk challenge加强版

题意

有一棵 \(n\) 个节点的树,每条边上有一个字符,有 \(m\) 次询问。

每次会选定两个点 \(u, v\)\(u\)\(v\) 的路径上的字符形成了一个字符串 \(T\) ,再选定一个字符串 \(S\) ,计算 \(S\)\(T\) 中的出现次数。

\(n, m \le 10^5, \sum |S| \le 5 \times 10^6\)

题解

原题多了一个保证 \(|S| \le 100\) 那么信息不会很多,直接倍增即可保存所有信息,复杂度是 \(?(m|S| + n|S| \log(n|S|) + m \log n(\log n + |S|))\)

显然对于 \(S\) 的长度限制是没有必要的。

询问一个串在另外一个串中出现的次数,查询串是给你的,但每次询问的时候不好把母串放进来跑,那么通常可以利用 \(SA/SAM\) 解决。

然后我们考虑是询问路径,我们进行树链剖分。那么我们只需要对于每个剖分的链,只需要询问这个串在其中一段出现多少次即可。

这可以用 \(SA\) + 主席树或者 \(SAM\) + 二维数点实现,都是单次询问 \(O(\log n)\) 的。

然后我们需要考虑跨轻边的部分如何计算,这一部分可以每次花 \(O(|S|)\) 的代价做 \(\mathrm{KMP}\) 匹配。

那么 \(SAM\) 做就是 \(O(n|\Sigma| + (m \log n + \sum |S|) \log n)\)\(SA\)\(O(n \log n + (m \log n+ \sum |S|)\log n)\)

其实还可以做到更优秀,我们对于整颗树建一颗广义 \(SAM\) 。(但是此处实现会有很多问题)

其中一种也许可行的实现方式是在 \(DFS\) 每次插入一个字母的时候,从父亲处继承 \(Last\) ,然后直接插入,这是本人的实现方式。

但他们说这样不仅复杂度可能会被卡到 \(O(n \sqrt n)\) 并且还有可能有正确性问题。

正确的建法是 \(BFS\) 逐层添加字符,这样就是复杂度和正确性都是正确的。

这样建完后,我们每次相当于查询整个 \(fail\) 树上上的一个子树在另外一颗数上 \(dfn\) 的一段连续区间。

那么这个用主席树合并实现就可以避免离线的细节了。

然后还有一个细节,有可能有些计算进去的串向上延伸会超过 \(lca\) ,那么一开始查询的时候限制一下它向上延伸的高度即可,此处树剖没有那么好实现,利用倍增即可。

这样的话,我们只需要在 \(lca\) 处计算跨链的贡献即可。

复杂度就优化成了 \(O(n|\Sigma| + n\log n + m \log^2 n + \sum |S|)\)

由于信息可减,查询链上信息。其实可以利用括号序列把复杂度优化成 \(O(n|\Sigma| + n\log n + m \log n + \sum |S|)\)

代码

\(SAM\) 处可能会存在问题,仅供参考。

最后就是 广义SAM + 主席树合并 + 树链剖分 + 倍增 + KMP 即可。

写了 \(6.7KB\) 。。。想死了。。。

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define pb push_back

using namespace std;

typedef unsigned long long ull;

template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }

inline int read() {
    int x(0), sgn(1); char ch(getchar());
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * sgn;
}

inline char read_char() {
    char ch(getchar());
    for (; !islower(ch); ch = getchar());
    return ch;
}

void File() {
    freopen ("e.in", "r", stdin);
    freopen ("e.out", "w", stdout);
}

const int N = 1e5 + 1e3, M = N << 1;

#define mid ((l + r) >> 1)
#define lson ls[o], l, mid
#define rson rs[o], mid + 1, r

template<int Maxn>
struct President_Tree {

    int ls[Maxn], rs[Maxn], sumv[Maxn], Size;

    void Update(int &o, int l, int r, int up) {
        if (!o) o = ++ Size;
        if (l == r) { ++ sumv[o]; return ; }
        up <= mid ? Update(lson, up) : Update(rson, up);
        sumv[o] = sumv[ls[o]] + sumv[rs[o]];
    }

    int Query(int o, int l, int r, int ql, int qr) {
        if (!o) return 0;
        if (ql <= l && r <= qr) return sumv[o];
        if (qr <= mid) return Query(lson, ql, qr);
        if (ql > mid) return Query(rson, ql, qr);
        return Query(lson, ql, qr) + Query(rson, ql, qr);
    }

    int Merge(int x, int y) {
        if (!x || !y) return x | y;
        int o = ++ Size;
        ls[o] = Merge(ls[x], ls[y]);
        rs[o] = Merge(rs[x], rs[y]);
        sumv[o] = sumv[x] + sumv[y]; return o;
    }

};

President_Tree<N * 120> T;

int n;

template<int Maxn, int Alpha>
struct Suffix_Automaton {

    int trans[Maxn][Alpha], link[Maxn], len[Maxn], num[Maxn], Size;

    Suffix_Automaton() { Size = 1; }

    inline int Extend(int Last, int id, int pos) {
        int cur = ++ Size, p = Last; len[cur] = len[Last] + 1; num[cur] = pos;
        for (; p && !trans[p][id]; p = link[p]) trans[p][id] = cur;
        if (!p) link[cur] = 1;
        else {
            int q = trans[p][id];
            if (len[q] == len[p] + 1) link[cur] = q;
            else {
                int clone = ++ Size;
                Cpy(trans[clone], trans[q]);
                len[clone] = len[p] + 1; link[clone] = link[q];
                for (; p && trans[p][id] == q; p  = link[p]) trans[p][id] = clone;
                link[cur] = link[q] = clone;
            }
        }
        return cur;
    }

    vector<int> G[Maxn]; int rt[Maxn];

    void Dfs(int u) {
        for (int v : G[u])
            Dfs(v), rt[u] = T.Merge(rt[u], rt[v]);
    }

    void Build() {
        For (i, 2, Size) {
            G[link[i]].pb(i); 
            if (num[i]) T.Update(rt[i], 1, n, num[i]);
        }
        Dfs(1);
    }

    int Trans(char *str) {
        int u = 1;
        For (i, 1, strlen(str + 1)) {
            int id = str[i] - 'a';
            if (!trans[u][id]) return 0;
            u = trans[u][id];
        }
        return u;
    }

};

Suffix_Automaton<N * 2, 26> SAM;

int Head[N], Next[M], to[M], val[M], e = 0;

inline void add_edge(int u, int v, int w) {
    to[++ e] = v; Next[e] = Head[u]; Head[u] = e; val[e] = w;
}

int anc[N][20];

int sz[N], son[N], pid[N], fa[N], dep[N], ch[N];

void Dfs_Init(int u, int from = 0) {
    sz[u] = 1; dep[u] = dep[anc[u][0] = fa[u] = from] + 1;
    for (int i = Head[u], v = to[i]; i; v = to[i = Next[i]]) 
        if (v != from) {
            ch[v] = val[i];
            Dfs_Init(v, u); sz[u] += sz[v];
            if (sz[v] > sz[son[u]]) son[u] = v;
        }
}

int dfn[N], top[N];

void Dfs_Part(int u) {
    static int clk = 0; dfn[u] = ++ clk;
    if (u > 1)
        pid[u] = SAM.Extend(pid[fa[u]], ch[u], clk);
    top[u] = son[fa[u]] == u ? top[fa[u]] : u;
    if (son[u]) Dfs_Part(son[u]);
    for (int i = Head[u], v = to[i]; i; v = to[i = Next[i]]) 
        if (v != fa[u] && v != son[u]) Dfs_Part(v);
}

int Lca(int x, int y) {
    for (; top[x] != top[y]; x = fa[top[x]])
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
    return dep[x] < dep[y] ? x : y;
}

int Calc(int x, int y, int pos) {
    int res = 0;
    for (; top[x] != top[y]; x = fa[top[x]]) {
        int l = dfn[top[x]], r = dfn[x];
        res += T.Query(SAM.rt[pos], 1, n, l, r);
    }
    return res + T.Query(SAM.rt[pos], 1, n, dfn[y], dfn[x]);
}

const int Len = 5e6 + 1e3;

char str[Len]; int Log2[N];

struct KMP {

    void Get_Fail(char *S, int *fail) {
        For (i, 2, strlen(S + 1)) {
            int j = fail[i - 1];
            while (j && S[j + 1] != S[i]) j = fail[j];
            fail[i] = S[j + 1] == S[i] ? j + 1 : 0;
        }
    }

    int Match(char *S, char *T, int *fail) {
        int lenT = strlen(T + 1), j = 0, res = 0;
        For (i, 1, strlen(S + 1)) {
            while (j && T[j + 1] != S[i]) j = fail[j];
            if (T[j + 1] == S[i]) ++ j;
            if (j == lenT) ++ res;
        }
        return res;
    }

};

KMP FUCK;

char S[Len], tmp[Len]; int fail[Len];

int MATCH(int x, int y) {

    int lca = Lca(x, y), len = 0, lt = 0;
    for (; x != lca; x = fa[x]) S[++ len] = ch[x] + 'a';
    for (; y != lca; y = fa[y]) tmp[++ lt] = ch[y] + 'a';
    reverse(tmp + 1, tmp + lt + 1);

    For (i, 1, lt) S[++ len] = tmp[i];
    FUCK.Get_Fail(str, fail); S[len + 1] = '\0';

    return FUCK.Match(S, str, fail);

}

int Ancestor(int x, int k) {
    Fordown (i, Log2[dep[x]], 0) 
        if (dep[anc[x][i]] >= k) x = anc[x][i];
    return x;
}

int Split(int x, int y, int lca) {
    int len = strlen(str + 1), gap = dep[lca] + (len - 1);
    if (len == 1) return 0;
    x = Ancestor(x, min(gap, dep[x]));
    y = Ancestor(y, min(gap, dep[y]));
    return MATCH(x, y);
}

int main () {

    File();

    n = read();
    For (i, 1, n - 1) {
        int u = read(), v = read(), ch = read_char() - 'a';
        add_edge(u, v, ch); add_edge(v, u, ch);
    }

    pid[1] = 1;

    Dfs_Init(1); Dfs_Part(1); SAM.Build();

    For (i, 2, n) Log2[i] = Log2[i >> 1] + 1;
    For (j, 1, Log2[n]) For (i, 1, n) 
        anc[i][j] = anc[anc[i][j - 1]][j - 1];

    int m = read();
    For (i, 1, m) {
        int u = read(), v = read(); scanf ("%s", str + 1);
        int len = strlen(str + 1);

        int lca = Lca(u, v), ans = 0, dis = dep[u] + dep[v] - 2 * dep[lca] + 1;
        if (len >= dis) { puts("0"); continue; }

        int gap = dep[lca] + len; 
        if (lca == u || lca == v) {
            if (v == lca) reverse(str + 1, str + len + 1);

            if (u == lca) swap(u, v);
            if (gap <= dep[u])
                ans = Calc(u, Ancestor(u, gap), SAM.Trans(str));
        } else {
            if (gap <= dep[v])
                ans = Calc(v, Ancestor(v, gap), SAM.Trans(str));
            reverse(str + 1, str + len + 1);
            if (gap <= dep[u])
                ans += Calc(u, Ancestor(u, gap), SAM.Trans(str));
            reverse(str + 1, str + len + 1);
            ans += Split(u, v, lca);
        }
        printf ("%d\n", ans);

    }

#ifdef zjp_shadow
    freopen ("/proc/self/status", "r", stdin);
    char str[1010];
    while (cin.getline(str, 1000))
        cerr << str << endl;
#endif

    return 0;

}

转载于:https://www.cnblogs.com/zjp-shadow/p/10209360.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,Codeforces Round 511 (Div. 1)是一个比赛的名称。然而,引用内容中没有提供与这个比赛相关的具体信息或问题。因此,我无法回答关于Codeforces Round 511 (Div. 1)的问题。如果您有关于这个比赛的具体问题,请提供更多的信息,我将尽力回答。 #### 引用[.reference_title] - *1* [Codeforces Round 860 (Div. 2)题解](https://blog.csdn.net/qq_60653991/article/details/129802687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces Round 867 (Div. 3)(A题到E题)](https://blog.csdn.net/wdgkd/article/details/130370975)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Codeforces Round 872 (Div. 2)(前三道](https://blog.csdn.net/qq_68286180/article/details/130570952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值