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; }