Luogu5211 [ZJOI2017]字符串

Luogu5211 [ZJOI2017]字符串

最小后缀理论、哈希、线段树

对于一个字符串的后缀,如果可能在它后面加入一个字符串之后,能够使该后缀能够成为最小后缀,我们设该后缀\(\in T\),那么\(|T|=O(\log n)\)

对于后缀\(U,V \in T,|U|<|V|\),则\(2|U|<|V|\)

上述结论证明见此处blog末尾

我们利用线段树直接维护区间的\(T\)集合,考虑合并。

当我们将左儿子和右儿子合并时,由于线段树的结构,右儿子长度\(\le\)左儿子长度\(\le\)右儿子长度\(+1\),根据上面的理论,左儿子中只可能有一个合法解。

我们可以不去维护完整的\(T\),而是直接从左儿子的集合中选择一个最小的,插入右儿子中,如果存在一个后缀是另一个后缀的前缀这种情况,我们选择最长的那个,原因见上述blog中的证明。

询问时,直接从所有可能的集合中选取即可。

我们需要动态维护哈希值,如果利用线段树,那么由于二分+\(hash\)求解过程,分析得时间复杂度高达\(O(q \log^4 n)\)。我们选择分块,即可将时间复杂度降为\(O(q \log^3 n+q \sqrt n)\)

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#define N 200005
#define orz vector<int>
#define IT orz :: iterator
#define ll long long
using namespace std;
const int p=999999797;
const int bs=277;
int n,q,blo,opt,l,r,d,ans,bel[N],bel2[N],L[N],R[N],pt[N],a[N],tag[N];
int m0[N],im0[N],h[N],qz[455][455],rqz[455][455],rq[455];
orz tr[N << 2];
int ksm(int x,int y)
{
    int ans(1);
    while (y)
    {
        if (y & 1)
            ans=(ll)ans*x%p;
        x=(ll)x*x%p;
        y >>=1;
    }
    return ans;
}
void Broken(int x)
{
    for (int i=L[x];i<=R[x];++i)
        a[i]+=tag[x];
    for (int i=x;i<=bel[n];++i)
        rq[i]=(rq[i]-h[x])%p;
    h[x]=tag[x]=0;
}
void Rebuild(int x)
{
    for (int i=L[x];i<=R[x];++i)
    {
        h[x]=((ll)h[x]+(ll)a[i]*m0[i])%p;
        qz[x][i-L[x]+1]=((ll)qz[x][i-L[x]]+(ll)a[i]*m0[i])%p;
    }
    for (int i=x;i<=bel[n];++i)
        rq[i]=(rq[i]+h[x])%p;
}
void fk_modify(int l,int r,int x)
{
    if (bel[l]==bel[r])
    {
        Broken(bel[l]);
        for (int i=l;i<=r;++i)
            a[i]+=x;
        Rebuild(bel[l]);
        return;
    }
    Broken(bel[l]);
    Broken(bel[r]);
    for (int i=l;i<=R[bel[l]];++i)
        a[i]+=x;
    for (int i=L[bel[r]];i<=r;++i)
        a[i]+=x;
    Rebuild(bel[l]),Rebuild(bel[r]);
    int s(0);
    for (int i=bel[l]+1;i<=bel[r]-1;++i)
    {
        tag[i]+=x;
        h[i]=((ll)h[i]+(ll)pt[i]*x)%p;
        s=((ll)s+(ll)pt[i]*x)%p;
        rq[i]=(rq[i]+s)%p;
    }
    for (int i=bel[r];i<=bel[n];++i)
        rq[i]=(rq[i]+s)%p;
}
int geta(int x)
{
    return a[x]+tag[bel[x]];
}
int calc(int x)
{
    return (((ll)rq[bel[x]-1]+(ll)qz[bel[x]][bel2[x]]+(ll)rqz[bel[x]][bel2[x]]*tag[bel[x]])%p+p)%p;
}
int calc(int l,int r)
{
    return ((ll)im0[l-1]*(calc(r)-calc(l-1))%p+p)%p;
}
void ckmin(int &ans,int x,int r)
{
    if (!ans)
    {
        ans=x;
        return;
    }
    int L(0),R(r-max(ans,x)+1),g(0);
    while (L<=R)
    {
        int mid(L+R >> 1);
        if (calc(ans,ans+mid-1)==calc(x,x+mid-1))
            g=mid,L=mid+1; else
            R=mid-1;
    }
    if (g==r-max(ans,x)+1)
        ans=(ans<x)?ans:x; else
        ans=(geta(ans+g)<geta(x+g))?ans:x;
}
void ckmin2(int &ans,int x,int r)
{
    if (!ans)
    {
        ans=x;
        return;
    }
    int L(0),R(r-max(ans,x)+1),g(0);
    while (L<=R)
    {
        int mid(L+R >> 1);
        if (calc(ans,ans+mid-1)==calc(x,x+mid-1))
            g=mid,L=mid+1; else
            R=mid-1;
    }
    if (g==r-max(ans,x)+1)
        ans=(ans>x)?ans:x; else
        ans=(geta(ans+g)<geta(x+g))?ans:x;
}
orz combine(int l,int r,orz A,orz B)
{
    int ans(0);
    for (IT it=A.begin();it!=A.end();++it)
        ckmin(ans,*it,r);
    B.push_back(ans);
    return B;
}
void build(int p,int l,int r)
{
    if (l==r)
    {
        tr[p].push_back(l);
        return;
    }
    int mid(l+r >> 1);
    build(p << 1,l,mid);
    build(p << 1 | 1,mid+1,r);
    tr[p]=combine(l,r,tr[p << 1],tr[p << 1 | 1]);
}
void modify(int p,int l,int r,int x,int y)
{
    if (l==x && r==y)
        return;
    int mid(l+r >> 1);
    if (y<=mid)
        modify(p << 1,l,mid,x,y); else
    if (x>mid)
        modify(p << 1 | 1,mid+1,r,x,y); else
        {
            modify(p << 1,l,mid,x,mid);
            modify(p << 1 | 1,mid+1,r,mid+1,y);
        }
    tr[p]=combine(l,r,tr[p << 1],tr[p << 1 | 1]);
}
void calc(int p,int l,int r,int x,int y,int z)
{
    if (l==x && r==y)
    {
        for (IT it=tr[p].begin();it!=tr[p].end();++it)
            ckmin2(ans,*it,z);
        return;
    }
    int mid(l+r >> 1);
    if (y<=mid)
        calc(p << 1,l,mid,x,y,z); else
    if (x>mid)
        calc(p << 1 | 1,mid+1,r,x,y,z); else
        {
            calc(p << 1,l,mid,x,mid,z);
            calc(p << 1 | 1,mid+1,r,mid+1,y,z);
        }
}
int main()
{
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    blo=sqrt(n);
    for (int i=1;i<=n;++i)
    {
        bel[i]=(i-1)/blo+1;
        if (bel[i]!=bel[i-1])
            L[bel[i]]=i,bel2[i]=1; else
            bel2[i]=bel2[i-1]+1;
        R[bel[i]]=i;
    }
    int iv(ksm(bs,p-2));
    m0[0]=1,im0[0]=1;
    for (int i=1;i<=n;++i)
        m0[i]=(ll)m0[i-1]*bs%p,im0[i]=(ll)im0[i-1]*iv%p;
    for (int i=1;i<=n;++i)
    {
        pt[bel[i]]=(pt[bel[i]]+m0[i])%p;
        h[bel[i]]=((ll)h[bel[i]]+(ll)a[i]*m0[i])%p;
        rqz[bel[i]][bel2[i]]=(rqz[bel[i]][bel2[i]-1]+m0[i])%p;
        qz[bel[i]][bel2[i]]=((ll)qz[bel[i]][bel2[i]-1]+(ll)a[i]*m0[i])%p;
    }
    for (int i=1;i<=bel[n];++i)
        rq[i]=(rq[i-1]+h[i])%p;
    build(1,1,n);
    for (int i=1;i<=q;++i)
    {
        scanf("%d",&opt);
        if (opt==1)
        {
            scanf("%d%d%d",&l,&r,&d);
            fk_modify(l,r,d);
            modify(1,1,n,l,r);
        } else
        {
            scanf("%d%d",&l,&r);
            ans=0;
            calc(1,1,n,l,r,r);
            printf("%d\n",ans);
        }
    }
    return 0;
}
posted @ 2021-04-13 16:23  GK0328  阅读(56)  评论(0编辑  收藏  举报