LG P5211 [ZJOI2017]字符串

Description

维护一个动态字符串 $s[1 \cdots n]$,字符串的字符集是所有 $|x| \leq 10^9$ 的整数。要求支持两个操作:

1) 输入 $l, r, d$,对于所有 $l \leq i \leq r$,将 $s[i]$ 修改为 $s[i] + d$,注意 $d$ 可能是负数。

2) 输入 $l, r$,输出子串 $s[l..r]$ 的字典序最小的后缀的起点位置。即,如果最小后缀是 $s[p..r],(l \leq p \leq r)$,请输出 $p$。

Solution

线段树每个节点维护以区间右端点为右端点,可能成为最小后缀(所有后缀减去一定不是最小后缀)的所有左端点

强制要去线段树的左区间大于等于右区间,按照以下的方式更新:

继承右区间的所有端点,从左区间中继承一个端点

将左区间保存的所有后缀后接上右区间子串,比较它们的大小,如果两个串因字母不同而比出大小,取字典序较小的;如果因长度不同而比出大小,取长度大的,因为:

设这个较长的串为$a$,较短的串为$b$,那么$b$是$a$的border,所以$a$是有周期的,所以某个左端点在右区间的后缀必是$a$的前缀,设其为$c$,如果$b$后接的字母比$a$中对应位置的大,$b$不是最小的;如果$b$后接的字母比$a$中对应位置的小,$c$就比$b$小,$b$不是最小的,所以此时取长度大的

所以每个线段树节点维护的左端点个数不超过$\log n$

并分块维护原串的哈希值,修改时间复杂度$O(m\sqrt n)$

询问时在线段树上找到区间,比较这些区间内所有后缀串的大小,时间复杂度$O(m \log^3 n)$

预处理建树时间复杂度$O(n \log^2 n)$,预处理分块哈希时间复杂度$O(n)$

代码抄的

#pragma GCC optimize(2)
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,q,s[200005],TP;
const int mod1=1e9+9,mod2=1e9+7,bas=998244353,B=300;
struct str{
    int st,len;
}ans;
inline int read(){
    int f=1,w=0;
    char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
    return f*w;
}
long long ksm(long long a,long long p,long long m){
    long long ret=1;
    while(p){
        if(p&1)(ret*=a)%=m;
        (a*=a)%=m,p>>=1;
    }
    return ret;
}
namespace has{
    int bi[200005],bj[200005];
    long long mi1[200005],mi2[200005],imi1[200005],imi2[200005],sf1[200005],sf2[200005],pre1[200005],pre2[200005];
    struct blk{
        int ch[305],siz,ad;
        long long sp1[305],sp2[305],pr1[305],pr2[305];
        int& operator [](const int& x){return ch[x];}
        void calh(){for(int i=1;i<=siz;i++)pr1[i]=(pr1[i-1]+mi1[i-1]*ch[i]%mod1+mod1)%mod1,pr2[i]=(pr2[i-1]+mi2[i-1]*ch[i]%mod2+mod2)%mod2;}
        void init(){
            for(int i=1;i<=siz;i++)sp1[i]=(sp1[i-1]+mi1[i-1])%mod1,sp2[i]=(sp2[i-1]+mi2[i-1])%mod2;
            calh();
        }
        long long gh1(int x){return (pr1[x]+sp1[x]*(ad+mod1))%mod1;}
        long long gh2(int x){return (pr2[x]+sp2[x]*(ad+mod2))%mod2;}
        void upd(int l,int r,int v){
            for(int i=1;i<=siz;i++)ch[i]+=ad;
            for(int i=l;i<=r;i++)ch[i]+=v;
            ad=0,calh();
        }
    }bl[705];
    void calc(int st){for(int i=st;i<=bi[n];i++)pre1[i]=(pre1[i-1]+bl[i].gh1(bl[i].siz)*sf1[i-1])%mod1,pre2[i]=(pre2[i-1]+bl[i].gh2(bl[i].siz)*sf2[i-1])%mod2;}
    void init(){
        for(int i=1;i<=n;i++)bi[i]=(i-1)/B+1,bj[i]=(i-1)%B+1;
        mi1[0]=mi2[0]=1,imi1[0]=imi2[0]=1,bl[bi[n]].siz=bj[n],sf1[0]=sf2[0]=1;
        for(int i=1;i<=n;i++)mi1[i]=mi1[i-1]*bas%mod1,mi2[i]=mi2[i-1]*bas%mod2;
        long long inv=ksm(bas,mod1-2,mod1);
        for(int i=1;i<=n;i++)imi1[i]=imi1[i-1]*inv%mod1;
        inv=ksm(bas,mod2-2,mod2);
        for(int i=1;i<=n;i++)imi2[i]=imi2[i-1]*inv%mod2;
        for(int i=1;i<=n;i++)bl[bi[i]][bj[i]]=s[i];
        for(int i=1;i<bi[n];i++)bl[i].siz=B;
        for(int i=1;i<=bi[n];i++)bl[i].init();
        for(int i=1;i<=bi[n];i++)sf1[i]=sf1[i-1]*mi1[bl[i].siz]%mod1,sf2[i]=sf2[i-1]*mi2[bl[i].siz]%mod2;
        calc(1);
    }
    long long gh1(int x){
        int t=bi[x]-1;
        return (pre1[t]+bl[t+1].gh1(bj[x])*sf1[t])%mod1;
    }
    long long gh2(int x){
        int t=bi[x]-1; 
        return (pre2[t]+bl[t+1].gh2(bj[x])*sf2[t])%mod2;
    }
    int gt(int x){return bl[bi[x]][bj[x]]+bl[bi[x]].ad;}
    bool check(int p1,int p2,int len){
        long long v1=(gh1(p1+len-1)+mod1-gh1(p1-1))*imi1[p1-1]%mod1,v2=(gh1(p2+len-1)+mod1-gh1(p2-1))*imi1[p2-1]%mod1;
        if(v1!=v2)return false;
        v1=(gh2(p1+len-1)+mod2-gh2(p1-1))*imi2[p1-1]%mod2,v2=(gh2(p2+len-1)+mod2-gh2(p2-1))*imi2[p2-1]%mod2;
        return v1==v2;
    }
    void update(int l,int r,int v){
        int p1=bi[l],p2=bi[r];
        if(p1==p2)bl[p1].upd(bj[l],bj[r],v);
        else{
            bl[p1].upd(bj[l],bl[p1].siz,v),bl[p2].upd(1,bj[r],v);
            for(int i=p1+1;i<p2;i++)bl[i].ad+=v;
        }
        calc(p1);
    }
}
bool operator <(str a,str b){
    int c1=has::gt(a.st),c2=has::gt(b.st),l=0,r=min(a.len,b.len),t=0;
    if(c1!=c2)return c1<c2;
    if(has::check(a.st,b.st,r))return (a.len>b.len)^TP;
    while(l<=r){
        int mid=l+r>>1;
        if(has::check(a.st,b.st,mid))l=mid+1,t=mid;
        else r=mid-1;
    }
    return has::gt(a.st+t)<has::gt(b.st+t);
}
str operator +(str a,int b){return (str){a.st,a.len+b};}
namespace tr{
    int cnt[800005];
    str v[800005][20];
    void pushup(int i,int len){
        cnt[i]=cnt[i<<1|1];
        for(int j=1;j<=cnt[i<<1|1];j++)v[i][j]=v[i<<1|1][j];
        str minn=v[i<<1][1]+len;
        for(int j=2;j<=cnt[i<<1];j++)minn=min(minn,v[i<<1][j]+len);
        v[i][++cnt[i]]=minn;
    }
    void build(int i,int l,int r){
        if(r-l==1){v[i][++cnt[i]]=(str){r,1};return;}
        int mid=l+r>>1;
        build(i<<1,l,mid),build(i<<1|1,mid,r),pushup(i,r-mid);
    }
    void update(int i,int l,int r,int L,int R){
        if(L<=l&&r<=R)return;
        int mid=l+r>>1;
        if(L<mid)update(i<<1,l,mid,L,R);
        if(R>mid)update(i<<1|1,mid,r,L,R);
        pushup(i,r-mid);
    }
    void query(int i,int l,int r,int L,int R,int p){
        if(L<=l&&r<=R){
            int j=1;
            if(ans.st==-1)ans=str{v[i][1].st,p-v[i][1].st+1},j=2;
            for(;j<=cnt[i];j++)ans=min(ans,(str){v[i][j].st,p-v[i][j].st+1});
            return;
        }
        int mid=l+r>>1;
        if(L<mid)query(i<<1,l,mid,L,R,p);
        if(R>mid)query(i<<1|1,mid,r,L,R,p);
    }
}
int main(){
    n=read(),q=read();
    for(int i=1;i<=n;i++)s[i]=read();
    has::init(),tr::build(1,0,n);
    for(;q;q--){
        int opt=read(),l=read(),r=read();
        if(opt==1){
            int d=read();
            TP=0,has::update(l,r,d),tr::update(1,0,n,l-1,r);
        }
        else TP=1,ans=(str){-1,0},tr::query(1,0,n,l-1,r,r),printf("%d\n",ans.st);
    }
    return 0;
}
[ZJOI2017]字符串

 

posted @ 2021-03-25 19:12  QDK_Storm  阅读(39)  评论(0编辑  收藏  举报