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