洛谷传送门
BZOJ传送门
题解:
写ZJOI真是作死的绝佳方式
于是一个下午只写了一道题。
首先是毒瘤至极的性质分析。
对于字符串 S S S的某个后缀 v v v,我们称它是 S S S的好后缀当且仅当存在某个字符串 T T T,使得 v T vT vT能够成为 S T ST ST的最小后缀。
通过一波莫名其妙让人吐血的分析我们可以知道 S S S的好后缀最多只有 O ( log ∣ S ∣ ) O(\log |S|) O(log∣S∣)个。
引理:若 a , b a,b a,b都是 S S S的好后缀,且 ∣ a ∣ > ∣ b ∣ |a|>|b| ∣a∣>∣b∣,必有 ∣ a ∣ ≥ 2 ∣ b ∣ |a|\geq 2|b| ∣a∣≥2∣b∣。
考虑证明,假设 ∣ a ∣ > ∣ b ∣ |a|>|b| ∣a∣>∣b∣,则 b b b必然为 a a a的前缀,若 ∣ a ∣ < 2 ∣ b ∣ |a|<2|b| ∣a∣<2∣b∣,则我们知道, a a a有一个长度为 ∣ a ∣ − ∣ b ∣ |a|-|b| ∣a∣−∣b∣的周期,不妨设 a = c d c d c , b = c d c a=cdcdc,b=cdc a=cdcdc,b=cdc,其中 c , d c,d c,d代表的都是不定字符串。
假设在加上字符串 t t t之后 b t bt bt成为了最小后缀,那么 a t > b t at>bt at>bt,即 c d c d c t > c d c t cdcdct>cdct cdcdct>cdct,注意到必然有 c d c t > c t cdct>ct cdct>ct,也就是 b t > c t bt>ct bt>ct,与前面假设 b t bt bt为最小后缀不符。
所以 ∣ S ∣ |S| ∣S∣的最小后缀长度是倍增的,最多只有 O ( log ∣ S ∣ ) O(\log |S|) O(log∣S∣)个。
我们考虑合并两个字符串 u , v u,v u,v,现在需要考虑这 O ( log u + log v ) O(\log u+\log v) O(logu+logv)个可能的最小后缀有哪些能够保留下来。直接拿所有位置作为新串的的后缀来比较就行了,如果比到后缀都不知道哪个更大的话,直接暴留更长的就行了。
现在还有一个问题:维护区间加和比较两个字符串大小,我们实际上还是可以二分 l c p lcp lcp然后比较哈希值,如果用线段树维护哈希值的话可能有点蛋疼,复杂度为 O ( n log 3 n + m log 4 n ) O(n\log^3 n+m\log^4 n) O(nlog3n+mlog4n)。
所以我们用分块维护哈希值,保证哈希值询问能够在 O ( 1 ) O(1) O(1)时间内回答,区间加在 O ( n ) O(\sqrt n) O(n)时间内完成,复杂度为 O ( n log 2 n + m log 3 n + m n ) O(n\log^2 n+m\log^3 n+m\sqrt n) O(nlog2n+mlog3n+mn)。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
char c;bool f=0;
while(!isdigit(c=gc()))f=c=='-';T num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return f?-num:num;
}
inline int getint(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
#define ull unsigned long long
cs int N=2e5+5;
int n,Q;
namespace HASH{
cs int SqrtN=500;
cs int base=1e9+7;
int bsiz,tot,lp[SqrtN],rp[SqrtN];
int blk[N],add[SqrtN],v[N];
ull pre[N],tag[SqrtN],pw[N],pws[N];
inline void init(){
bsiz=sqrt(n);
for(int re i=1;i<=n;++i){
if((i-1)%bsiz==0){rp[tot]=i-1;lp[++tot]=i;}
blk[i]=tot;
}
pw[0]=pws[0]=1;
for(int re i=1;i<=n;++i){
v[i]=getint()+5e8;
pw[i]=pw[i-1]*base;
pws[i]=pws[i-1]+pw[i];
pre[i]=pre[i-1]+v[i]*pw[i];
}
}
inline int val(int p){return v[p]+add[blk[p]];}
inline ull prefix(int p){
return pre[p]+tag[blk[p]]+add[blk[p]]*(pws[p]-pws[lp[blk[p]]-1]);
}
inline void modify(int l,int r,int d){
if(blk[l]!=blk[r]){
for(int re i=l,li=rp[blk[l]];i<=li;++i){
v[i]+=d;
pre[i]+=d*(pws[i]-pws[l-1]);
}
for(int re i=r,li=lp[blk[r]];i>=li;--i){
v[i]+=d;
pre[i]+=d*(pws[i]-pws[l-1]);
}
for(int re i=blk[l]+1,li=blk[r]-1;i<=li;++i){
add[i]+=d;
tag[i]+=d*(pws[lp[i]-1]-pws[l-1]);
}
}
else {
for(int re i=l;i<=r;++i){
v[i]+=d;
pre[i]+=d*(pws[i]-pws[l-1]);
}
}
ull delta=d*(pws[r]-pws[l-1]);
for(int re i=r+1,li=rp[blk[r]];i<=li;++i)pre[i]+=delta;
for(int re i=blk[r]+1;i<=tot;++i)tag[i]+=delta;
}
inline bool cmp(int x,int y,int l){
if(x>y)std::swap(x,y);
return (prefix(x+l-1)-prefix(x-1))*pw[y-x]==prefix(y+l-1)-prefix(y-1);
}
inline int lcp(int x,int y){
if(x>y)std::swap(x,y);
int l=0,r=n-y+1,d=y-x;
ull lx=prefix(--x);
ull ly=prefix(--y);
while(l<r){
int mid=l+r+1>>1;
((prefix(x+mid)-lx)*pw[d]==prefix(y+mid)-ly)?(l=mid):(r=mid-1);
}
return l;
}
}
namespace SGT{
struct atom{
int l,r;
std::vector<int> pos;
friend atom operator+(cs atom &a,cs atom &b){
assert(a.r==b.l-1);
atom ans;ans.l=a.l,ans.r=b.r;
std::vector<int> &p=ans.pos;
cs std::vector<int> &ap=a.pos,&bp=b.pos;
for(int re i=0,x;i<ap.size();++i){x=ap[i];
bool flag=true;
while(!p.empty()){
int y=p.back();
if(HASH::cmp(x,y,b.r-x+1))break;
int lcp=HASH::lcp(x,y);
if(HASH::val(x+lcp)>HASH::val(y+lcp)){flag=false;break;}
else p.pop_back();
}
if(flag&&(p.empty()||b.r-x+1<=x-p.back()))p.push_back(x);
}
for(int re i=0,x;i<bp.size();++i){x=bp[i];
bool flag=true;
while(!p.empty()){
int y=p.back();
if(HASH::cmp(x,y,b.r-x+1))break;
int lcp=HASH::lcp(x,y);
if(HASH::val(x+lcp)>HASH::val(y+lcp)){flag=false;break;}
else p.pop_back();
}
if(flag&&(p.empty()||b.r-x+1<=x-p.back()))p.push_back(x);
}
return ans;
}
};
atom a[N<<2];
#define lc k<<1
#define rc k<<1|1
inline void build(int k,int l,int r){
if(l==r){
a[k].l=l;
a[k].r=r;
a[k].pos.push_back(l);
return ;
}
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
a[k]=a[lc]+a[rc];
}
inline void modify(int k,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return ;
int mid=l+r>>1;
if(ql<=mid)modify(lc,l,mid,ql,qr);
if(mid<qr)modify(rc,mid+1,r,ql,qr);
a[k]=a[lc]+a[rc];
}
inline atom query(int k,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return a[k];
int mid=l+r>>1;
if(qr<=mid)return query(lc,l,mid,ql,qr);
if(mid<ql)return query(rc,mid+1,r,ql,qr);
return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}
inline int query(int l,int r){
return query(1,1,n,l,r).pos.back();
}
}
signed main(){
// freopen("string.in","r",stdin);
n=getint();Q=getint();
HASH::init();
SGT::build(1,1,n);
while(Q--)switch(getint()){
case 1:{
int l=getint(),r=getint(),d=getint();
HASH::modify(l,r,d);
SGT::modify(1,1,n,l,r);
break;
}
case 2:{
int l=getint(),r=getint();
cout<<SGT::query(l,r)<<"\n";
break;
}
}
return 0;
}