JZOJ4617 【NOI2016模拟7.12】可持久化字符串 建Trie后用主席树维护next数组

题目大意

现在要求可持久地维护一个字符集大小为 M 字符串S,可以支持对历史版本的询问。现在有 N 个操作,对于第i个操作包含两个整数 u,v ,表示在第 u 个版本的字符串末尾加上字符v作为第 i 个版本的字符串,并且输出每次操作之后的字符串的最短循环节长度。强制在线。

N,M3105

解题思路

首先要知道的是对于一个字符串 S ,它的最小循环节的长度为最大的t,使 t 满足既是S的前又是 S 的后缀。也就相当于KMP next 数组,更形象的说一个长度为 len 的的串,它的最小循环节等于 lennextlen

然后我们发现题目实际上要求我们维护一颗 Trie ,并且对于每条到根的链维护出 next 数组。那么复杂度肯定会有问题。那么我们考虑一样另一种方式维护这个东西。设 T(x,y) 表示在 x 到根的路径上,如果下一个字符为y那么根据 next 数组的定义会跳到哪个节点上, Lenx 表示当前这个节点到跟所代表字符串的长度(也就是在 Trie 上的深度)。我们看当前要进行第 i 次操作,插入的字符为c,要在第 u 个版本后插入。那么毫无疑问Ans=LeniLenT(u,c)。那么现在的问题就是怎么维护这个 T 数组。

那么考虑T(i) T(T(u,c)) 的关系。我们设节点 T(u,c) 在节点 i 方向的字符边是c,节点是 k ,那么T(i) T(T(u,c)) 不同的就只有关于 c 的转移,那么就是说 T(i)=T(T(u,c)) T(i,c)=k 。这个可以用主席树维护,那么就可以在 Log 的时间转移和维护。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 3e5 + 5;

struct Node {
    int l, r, Num;
} Tr[MAXN * 20];

int tot, Ans, N, M, Ord, Fa[MAXN][20], Len[MAXN], Root[MAXN], A[MAXN];

int Query(int Rt, int l, int r, int Side) {
    if (!Rt) return 0;
    if (l == r) return Tr[Rt].Num;
    int Mid = (l + r) >> 1;
    if (Side <= Mid) return Query(Tr[Rt].l, l, Mid, Side); else
        return Query(Tr[Rt].r, Mid + 1, r, Side);
}

void Modify(int &Rt, int rt, int l, int r, int Side, int Poi) {
    Rt = ++ tot;
    Tr[Rt] = Tr[rt];
    if (l == r) {
        Tr[Rt].Num = Poi;
        return;
    }
    int Mid = (l + r) >> 1;
    if (Side <= Mid) Modify(Tr[Rt].l, Tr[rt].l, l, Mid, Side, Poi); else
        Modify(Tr[Rt].r, Tr[rt].r, Mid + 1, r, Side, Poi);
}

int main() {
    scanf("%d%d%d", &N, &M, &Ord);
    for (int i = 1; i <= N; i ++) {
        int u, c;
        scanf("%d%d", &u, &c);
        if (Ord == 1) u ^= Ans, c ^= Ans;
        A[i] = c;
        Fa[i][0] = u;
        for (int j = 1; j < 20; j ++) Fa[i][j] = Fa[Fa[i][j - 1]][j - 1];
        Len[i] = Len[u] + 1;
        int Side = Query(Root[u], 1, M, c);
        Ans = Len[i] - Len[Side];
        printf("%d\n", Ans);
        int p = i, Up = Ans - 1;
        for (int j = 19; j + 1; j --)
            if (Len[i] - Len[Fa[p][j]] <= Up) p = Fa[p][j];
        Modify(Root[i], Root[Side], 1, M, A[p], p);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值