LOJ 6041 「雅礼集训 2017 Day7」事情的相似度

LCT+SAM+线段树

LibreOJ开放数据很资瓷啊!
但是我想问为什么首页上我的运势总是凶或者大凶啊???

问一个区间里的前缀之间的最长公共后缀,考虑SAM一发,建出反串后缀树,然后求最长公共后缀就变成求最深的LCA。

直接维护区间的答案不好做,因此考虑一些离线的算法。莫队不知道能不能做,这里考虑把询问按照右端点排序,然后从左到右扫一遍。

扫到i,则维护所有左端点在1~i-1,右端点在i的区间的答案。i新贡献的答案就是i和1~i-1的最公共后缀,也就是后缀树上一个点和其它一些点的最深LCA。考虑在后缀树上的每个节点维护这个节点对应串中位置的最大下标(后面的位置显然不劣于前面的),再维护一个子树最大值。则i往根走的过程中这个值单调不降。暴力枚举每一段来更新答案,用线段树维护区间答案,最后再把根到i的值全设为i即可。

暴力枚举的复杂度不是炸了吗?仔细观察这个过程,发现它是个access,时间复杂度什么就让它去见LCT吧。

#include<cstdio>
#include<vector>
#define N 100005
#define cmax(_i,_j) ((_i)<(_j)?(_i)=(_j):0)
using namespace std;
namespace runzhe2000
{
    struct data{int l, id;};
    vector<data> que[N];

    int n, m, ans[N];
    char s[N];

    struct seg{int v;}t[N<<2];
    void build(int x, int l, int r)
    {
        t[x].v = 0; if(l == r) return; int mid = (l+r)>>1;
        build(x<<1,l,mid); build(x<<1|1,mid+1,r); 
    }
    void modi(int x, int l, int r, int p, int v)
    {
        if(l == r) return void(cmax(t[x].v, v)); int mid = (l+r)>>1;
        p <= mid ? modi(x<<1,l,mid,p,v) : modi(x<<1|1,mid+1,r,p,v);
        t[x].v = max(t[x<<1].v, t[x<<1|1].v);
    }
    int query(int x, int l, int r, int ql, int qr)
    {
        if(ql <= l && r <= qr) return t[x].v; int mid = (l+r)>>1, ret = 0;
        if(ql <= mid) ret = max(ret, query(x<<1,l,mid,ql,qr));
        if(mid <  qr) ret = max(ret, query(x<<1|1,mid+1,r,ql,qr));
        return ret;
    }

    struct node
    {
        node *ch[2], *fa;
        int v, dep;
    }mem[N<<1], *tot, *null;
    node *newnode()
    {
        node *p = ++tot; *p = *null;
        return p;
    }
    int type(node *x){return x->fa->ch[1]==x;}
    int isroot(node *x){return x->fa->ch[type(x)] != x;}
    void update(node *x)
    {
        if(!isroot(x)) update(x->fa);
        if(x->ch[0] != null) x->ch[0]->v = x->v;
        if(x->ch[1] != null) x->ch[1]->v = x->v;
    }
    void rotate(node *x)
    {
        node *f = x->fa; int d = type(x);
        x->fa = f->fa, !isroot(f) ? x->fa->ch[type(f)] = x : 0;
        (f->ch[d] = x->ch[!d]) != null ? f->ch[d]->fa = f : 0;
        x->ch[!d] = f, f->fa = x;
    }
    void splay(node *x)
    {
        for(update(x); !isroot(x); )
        {
            if(isroot(x->fa)) rotate(x);
            else if(type(x) == type(x->fa)) rotate(x->fa), rotate(x);
            else rotate(x), rotate(x);
        }
    }
    void access(node *x)
    {
        node *tmp = null;
        for(; x != null; )
        {
            splay(x);
            if(x->v)
                modi(1,1,n,x->v,x->dep);
            x->ch[1] = tmp;
            tmp = x;
            x = x->fa;
        }
    }

    struct sam
    {
        sam *next[2], *fail;
        int len; node *pos;
    }mem2[N<<1], *tot2, *null2, *root, *pre[N];
    sam *newsam()
    {
        sam *p = ++tot2; *p = *null2; 
        return p;
    }
    void extend(int i)
    {
        int v = s[i] - '0'; 
        sam *p = pre[i-1], *np = pre[i] = newsam(); np->len = p->len + 1;
        for(; p->next[v] == null2 && p != null2; p = p->fail) p->next[v] = np;
        if(p == null2) np->fail = root;
        else
        {
            if(p->next[v]->len == p->len + 1) np->fail = p->next[v];
            else
            {
                sam *q = p->next[v], *nq = newsam(); *nq = *q; nq->len = p->len + 1;
                q->fail = np->fail = nq;
                for(; p->next[v] == q && p != null2; p = p->fail) p->next[v] = nq;
            }   
        }
    }

    void init()
    {
        null = tot = mem; *null = (node){null,null,null,0,0};

        null2 = tot2 = mem2; *null2 = (sam){{null2,null2},null2,0};
        pre[0] = root = newsam();
    }

    void main()
    {
        scanf("%d%d%s",&n,&m,s+1);
        for(int i = 1; i <= m; i++)
        {
            int l, r; scanf("%d%d",&l,&r);
            que[r].push_back((data){l,i});
        }
        init(); build(1,1,n);
        for(int i = 1; i <= n; i++) extend(i);
        for(sam *p = tot2; p != mem2; p--)
        {
            p->pos = newnode();
            p->pos->v = 0;
            p->pos->dep = p->len;
        }
        for(sam *p = tot2; p != root; p--) 
        {
            p->pos->fa = p->fail->pos;
        }
        for(int i = 1; i <= n; i++)
        {
            node *p = pre[i]->pos; access(p); splay(p); p->v = i;
            for(int j = 0, jj = que[i].size(); j < jj; j++) ans[que[i][j].id] = query(1,1,n,que[i][j].l,n);
        }
        for(int i = 1; i <= m; i++) printf("%d\n",ans[i]);
    }
}
int main()
{
    runzhe2000::main();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值