51Nod 1600 Simple KMP

SAM+LCT/树剖线段树

一个点的深度就是有多少个以这个点结尾的后缀等于前缀。fail树性质不太够,不好下手,直接考虑答案的贡献。对于两个相等的区间[l,r]&[x,y]其中l

#include<cstdio>
#include<cstring>
#include<iostream>
#define N 100005
#define MOD 1000000007
#define A 28
#define pr(_i) cout<<#_i<<" = "<<_i<<endl
using namespace std;
namespace runzhe2000
{
    typedef long long ll;
    int n, ecnt, last[N<<1], q[N], top[N<<1], siz[N<<1], son[N<<1], dep[N<<1], fa[N<<1], beg[N<<1], rebeg[N<<1], timer;
    char s[N];
    struct edge{int next, to;}e[N<<1];
    void addedge(int a, int b)
    {
        e[++ecnt] = (edge){last[a], b};
        last[a] = ecnt;
    }

    struct SAM
    {
        SAM *fail, *next[A];
        int len;
    }mem[N<<1], *tot, *null, *lastp, *root;
    SAM *newSAM(){SAM *p = ++tot; *p = *null; return p;} 
    void init_SAM()
    {
        null = tot = mem;
        null->fail = null;
        for(int i = 0; i < A; i++) null->next[i] = null;
        lastp = root = newSAM();
    }
    void extend(int v)
    { 
        SAM *p = lastp, *np = newSAM(); lastp = np; np->len = p->len + 1;
        for(; p->next[v] == null && p != null; p = p->fail) p->next[v] = np;
        if(p == null) np->fail = root;
        else
        {
            SAM *q = p->next[v];
            if(p->len + 1 == q->len)np->fail = q;
            else
            {
                SAM *nq = newSAM(); nq->len = p->len + 1;
                memcpy(nq->next, q->next, sizeof(q->next));
                nq->fail = q->fail; q->fail = np->fail = nq;
                for(; p->next[v] == q && p != null; p = p->fail) p->next[v] = nq;
            }
        }
    }   

    struct seg
    {
        int sum, tag1, tag2, lazy;
    }t[N*8];
    void pushup(int x)
    {
        t[x].sum = (t[x<<1].sum + t[x<<1|1].sum) % MOD;
        t[x].tag1 = (t[x<<1].tag1 + t[x<<1|1].tag1) % MOD;
        t[x].tag2 = (t[x<<1].tag2 + t[x<<1|1].tag2) % MOD;
    }
    void build(int x, int l, int r)
    {
        if(l == r){t[x].tag1 = -(t[x].tag2 = mem[rebeg[l]].len - mem[rebeg[l]].fail->len); return;}
        int mid = (l+r)>>1; build(x<<1,l,mid); build(x<<1|1,mid+1,r); 
        pushup(x);
    }
    void add(int x, int c)
    {
        (t[x].sum += ((ll)t[x].tag2*c%MOD*c%MOD + (ll)t[x].tag1*c%MOD)*(MOD-MOD/2)%MOD) %= MOD;
        (t[x].tag1 += (ll)t[x].tag2*c%MOD*2%MOD) %= MOD; (t[x].lazy += c) %= MOD;
    }
    void pushdown(int x)
    {
        if(!t[x].lazy) return;
        add(x<<1,t[x].lazy); add(x<<1|1,t[x].lazy);
        t[x].lazy = 0;
    }
    void modi(int x, int l, int r, int ql, int qr, int c)
    {
        if(ql <= l && r <= qr) return (void)add(x,c); int mid = (l+r)>>1; pushdown(x);
        if(ql <= mid) modi(x<<1,l,mid,ql,qr,c); if(mid < qr) modi(x<<1|1,mid+1,r,ql,qr,c);
        pushup(x);      
    }

    void dfs1(int x)
    {
        dep[x] = dep[fa[x]] + 1; siz[x] = 1;
        for(int i = last[x]; i; i = e[i].next)
        {
            int y = e[i].to; if(y == fa[x]) continue;
            fa[y] = x; dfs1(y); siz[x] += siz[y];
            siz[y] > siz[son[x]] ? son[x] = y : 0;
        }
    }
    void dfs2(int x)
    {
        rebeg[beg[x] = ++timer] = x; 
        top[x] = son[fa[x]] == x ? top[fa[x]] : x;
        if(son[x]) dfs2(son[x]);
        for(int i = last[x]; i; i = e[i].next)
        {
            int y = e[i].to; if(y == fa[x] || y == son[x]) continue;
            dfs2(y);
        }
    }

    void main()
    {
        scanf("%d%s",&n,s+1);
        init_SAM();
        for(int i = 1; i <= n; i++)
        {
            extend(s[i] - 'a');
            q[i] = lastp - mem;
        }
        for(SAM *i = tot; i != mem; i--) addedge(i->fail - mem, i - mem);
        dfs1(1); dfs2(1); int totmem = tot-mem; build(1,1,totmem);
        int ans = 0;
        for(int i = 1; i <= n; i++)
        {
            int x = q[i];
            for(; x; x = fa[top[x]]) modi(1,1,totmem,beg[top[x]],beg[x], 1);
            (ans += t[1].sum) %= MOD;
            printf("%d\n",(ans+MOD)%MOD);
        }
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值