「雅礼集训 2017 Day7」事情的相似度 SAM+LCT+SEG

4 篇文章 0 订阅

https://loj.ac/problem/6041
题意:给出一个01串,求出结束点在 [ l , r ] [l,r] [l,r]的两个前缀的最长公共后缀长度
i , j i, j i,j 表示原字符串上位置, p o s [ ] pos[] pos[] 表示在SAM上对应的点
求两个点的最长公共后缀长度,就是他们在后缀树上的LCA点的 m x l mxl mxl
先把询问离线下来,按右端点排序,从左向右顺着扫一遍,每次把当前点 p o s [ i ] pos[i] pos[i] 到根节点的路径都打上 i i i 位置标记,如果新加入的点到根的路径上找到了标记,那么就要更新这个点的 m x l mxl mxl 对答案的贡献
p o s [ i ] pos[i] pos[i] 到根节点的路径就是LCT的access操作,顺便找出打过标记的点
在这里插入图片描述
如图,当前右端点已经到了 r r r 在进行access操作时,找到了 j j j 节点,那么在线段树上 j j j 的mx值就可以更新成 m x l mxl mxl ,在线段树上查询区间 [ l , r ] [l,r] [l,r] 时,当 l l l j j j 的右边时,这个 m x l mxl mxl 不会对答案贡献,当 l l l j j j 的左边时,这个 m x l mxl mxl 可能对答案贡献。所以只需要在线段树上更新 j j j 的mx值
当结束完一个点的access操作和query后,再对它打上标记

#include <bits/stdc++.h>
#define ls (p << 1)
#define rs (p << 1 | 1)
#define lc ch[x][0]
#define rc ch[x][1]
using namespace std;
inline void read(int &x){
    x = 0; int f = 1; char ch = getchar();
    while (!(ch >= '0' && ch <= '9')){if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
inline void Max(int &x, int y){if (y > x) x = y;}
const int N = 2e5 + 10;
int n, m;
char s[N];
int tot, pos[N];
struct Query{int l, r, id;} Q[N];
inline bool cmp(Query a, Query b){return a.r == b.r ? a.l < b.l : a.r < b.r;}
struct Seg{
    int mx[N * 8];
    inline void pushup(int p){mx[p] = max(mx[ls], mx[rs]);}
    inline void update(int p, int ps, int v, int l = 1, int r = n){
        if (l == r){Max(mx[p], v); return;}
        int mid = (l + r) >> 1;
        if (ps <= mid) update(ls, ps, v, l, mid);
        else update(rs, ps, v, mid + 1, r);
        pushup(p);
    }
    inline int query(int p, int ll, int rr, int l = 1, int r = n){
        if (ll == l && rr == r) return mx[p];
        int mid = (l + r) >> 1;
        if (rr <= mid) return query(ls, ll, rr, l, mid);
        else if (ll > mid) return query(rs, ll, rr, mid + 1, r);
        else return max(query(ls, ll, mid, l, mid), query(rs, mid + 1, rr, mid + 1, r));
    }
}seg;
struct SAM{
    int ch[N][2], pre[N], mxl[N], last, cnt;
    SAM(){last = cnt = 1;}
    inline void Extend(int c, int i){
        int p = last, np = ++cnt; last = np; pos[i] = np;
        for (; p && !ch[p][c]; p = pre[p]) ch[p][c] = np;
        if (!p){pre[np] = 1; return;}
        int q = ch[p][c], nq = ++cnt;
        if (mxl[q] == mxl[p] + 1){pre[np] = q; --cnt; return;}
        memcpy(ch[nq], ch[q], sizeof(ch[q]));
        mxl[nq] = mxl[p] + 1; pre[nq] = pre[q], pre[q] = pre[np] = nq;
        for (; ch[p][c] == q; p = pre[p]) ch[p][c] = nq;
    }
}sam;
struct LCT{
    int ch[N][2], fa[N], pd[N], val[N];
    inline bool chk(int x){return x == ch[fa[x]][1];}
    inline bool nroot(int x){return x == ch[fa[x]][chk(x)];}
    inline void pushdown(int x){
        if (pd[x]){
            if (lc) val[lc] = pd[lc] = pd[x];
            if (rc) val[rc] = pd[rc] = pd[x];
            pd[x] = 0;
        }
    }
    inline void Rotate(int x){
        int y = fa[x], z = fa[y], k = chk(x), w = ch[x][k ^ 1];
        if (nroot(y)) ch[z][chk(y)] = x;
        ch[y][k] = w, ch[x][k ^ 1] = y;
        if (w) fa[w] = y;
        fa[y] = x, fa[x] = z;
    }
    inline void pushall(int x){if (nroot(x)) pushall(fa[x]); pushdown(x);}
    inline void splay(int x){
        pushall(x);
        while (nroot(x)){
            int y = fa[x];
            if (nroot(y)) Rotate((chk(x) ^ chk(y)) ? x : y);
            Rotate(x);
        }
    }
    inline void access(int x){
        for (int y = 0; x; x = fa[y = x]){
            splay(x);
            if (val[x]) seg.update(1, val[x], sam.mxl[x]);
            rc = y;
        }
    }
}lct;
int ans[N];
int main(){
    read(n), read(m);
    scanf("%s", s + 1);
    for (int i = 1; i <= n; i++) sam.Extend(s[i] - '0', i);
    for (int i = 1; i <= m; i++) read(Q[i].l), read(Q[i].r), Q[i].id = i;
    sort(Q + 1, Q + 1 + m, cmp);
    for (int i = 2; i <= sam.cnt; i++) lct.fa[i] = sam.pre[i];
    for (int i = 1, j = 1; i <= n; i++){
        lct.access(pos[i]);
        while (j <= m && Q[j].r == i) ans[Q[j].id] = seg.query(1, Q[j].l, Q[j].r), ++j;
        lct.splay(pos[i]), lct.val[pos[i]] = lct.pd[pos[i]] = i;
    }
    for (int i = 1; i <= m; i++) printf("%d\n", ans[i]);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值