传送门
求支持操作:
①在原串后加一个字符。
②询问某个区间内的不同子串个数。
由不同子串个数想到后缀自动机,然而并不好维护。
一个神奇的操作:用区间总子串个数减去重复的子串个数。
(收集新套路)
如何统计重复的子串个数?
我们只保留每一个串在区间中出现的最后一个位置就行了,也就是
e
n
d
p
o
s
endpos
endpos最大的一个位置。对于其他的
e
n
d
p
o
s
endpos
endpos,在开头的位置打一个减一标记。询问区间
[
l
,
r
]
[l,r]
[l,r]的时候统计区间和即可。
如何打标记?
而当新加入一个字符,它到根节点的路径上(
p
a
r
e
n
t
parent
parent树)的节点的
e
n
d
p
o
s
endpos
endpos会新增一个当前字符的位置,成为这些节点的保留位置。而在更新这个保留位置之前,他们有一个之前的保留位置。把这些之前的保留位置对应的开头在主席树上打上标记,然后再把它们的保留位置更新为当前位置即可。
有如下几个坑点(手残+脑残):
由于在 a c c e s s access access的时候, q q q和 f a [ q ] fa[q] fa[q]之间的实边会断掉,那么先 a c c e s s access access当前节点,再把 q q q的父亲指针改掉就好了,就不用写 c u t cut cut了
然后要注意的一点是根节点的长度为0,空节点的长度为0,当 a c c e s s access access到根节点的时候会导致左区间大于右区间。所以要留心主席树的询问操作写法。
在 S A M SAM SAM中的克隆节点的部分,当修改 D A G DAG DAG中指向 q q q的边时, p p p是已经变了的,所以要在这之前把 l e n [ c l o n e ] = l e n [ p ] + 1 len[clone]=len[p]+1 len[clone]=len[p]+1写了。。
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
cs int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
inline char peek(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1;}
inline char ga(){while(!isalpha(peek()))++p1;return *p1++;}
template<typename T>
inline T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int gi(){return get<int>();}
inline ll gl(){return get<ll>();}
}
using namespace IO;
cs int N=1e5+7;
int M;
namespace PST{
cs int N=::N*80;
int lc[N],rc[N],tag[N],tot;ll sum[N];
inline int cpy(int u){++tot;lc[tot]=lc[u],rc[tot]=rc[u],tag[tot]=tag[u],sum[tot]=sum[u];return tot;}
inline void update(int &u,int l,int r,int ql,int qr,int val){
u=cpy(u),sum[u]+=1ll*(std::min(r,qr)-std::max(l,ql)+1)*val;
if(ql<=l&&r<=qr){tag[u]+=val;return;}
int mid=l+r>>1;
if(ql<=mid) update(lc[u],l,mid,ql,qr,val);
if(qr> mid) update(rc[u],mid+1,r,ql,qr,val);
}
inline ll query(int u,int l,int r,int ql,int qr,ll ret=0){
if(!u) return 0;if(ql<=l&&r<=qr) return sum[u];
int mid=l+r>>1;ll laz=1ll*(std::min(r,qr)-std::max(l,ql)+1)*tag[u];
if(qr<=mid) return laz+query(lc[u],l,mid,ql,qr);
if(ql> mid) return laz+query(rc[u],mid+1,r,ql,qr);
return laz+query(lc[u],l,mid,ql,mid)+query(rc[u],mid+1,r,mid+1,qr);
}
}
int rt[N],len[N<<1];
namespace LCT{
cs int N=::N<<1;
int fa[N],son[N][2],tag[N],last[N];
inline void pushnow(int x,int v){tag[x]=last[x]=v;}
inline int isroot(int x){return (son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x);}
inline int get(int x){return x==son[fa[x]][1];}
inline void pushdown(int x){if(tag[x]) pushnow(son[x][0],tag[x]),pushnow(son[x][1],tag[x]),tag[x]=0;}
inline void rotate(int x){
int y=fa[x],z=fa[y],k=get(x),l=son[x][k^1];
fa[x]=z;if(!isroot(y)) son[z][get(y)]=x;
fa[y]=x,son[x][k^1]=y,fa[l]=y,son[y][k]=l;
}
inline void splay(int x){
static int st[N],top;st[top=1]=x;
for(int re i=x;!isroot(i);i=fa[i]) st[++top]=fa[i];
while(top) pushdown(st[top--]);
for(int re y=fa[x];!isroot(x);rotate(x),y=fa[x])
if(!isroot(y)) rotate(get(y)==get(x)?y:x);
}
inline void access(int x,int id){
int y=0;for(;x;x=fa[y=x]){
splay(x);if(last[x])
PST::update(rt[id],1,M,last[x]-len[x]+1,last[x]-len[fa[x]],1);
son[x][1]=y;
}pushnow(y,id);
}
inline int get_last(int x){splay(x);return last[x];}
inline void link(int u,int f){splay(u),fa[u]=f;}
}
using LCT::link;
using LCT::access;
int len_all;
namespace SAM{
cs int N=::N<<1|1;
int son[N][26],fa[N],sz=1,last=1;
inline void push_back(int c){
int id=++len_all,cur=++sz,p=last;rt[id]=rt[id-1],last=cur;
len[cur]=len[p]+1;
for(;p&&!son[p][c];p=fa[p])son[p][c]=cur;
if(!p) link(cur,fa[cur]=1),access(cur,id);
else{
int q=son[p][c];
if(len[q]==len[p]+1) link(cur,fa[cur]=q),access(cur,id);
else{
int clone=++sz;
memcpy(son[clone],son[q],sizeof son[q]),len[clone]=len[p]+1;
for(;p&&son[p][c]==q;p=fa[p])son[p][c]=clone;
LCT::last[clone]=LCT::get_last(q);
link(clone,fa[clone]=fa[q]),link(cur,fa[cur]=clone);
access(cur,id),link(q,fa[q]=clone);
}
}
}
}
ll ans;
int n,m,op,kind,l,r;
char s[N];
signed main(){
scanf("%d%s%d",&op,s+1,&m),M=(n=strlen(s+1))+m;
for(int re i=1;i<=n;++i)SAM::push_back(s[i]-'a');
while(m--){
kind=gi();
if(kind==1) SAM::push_back((ans*op+gc()-'a')%26);
if(kind==2){
l=(ans*op+gi()-1)%len_all+1,r=(ans*op+gi()-1)%len_all+1;
printf("%lld\n",ans=(1ll*(r-l+2)*(r-l+1)/2-PST::query(rt[r],1,M,l,r)));
}
}
}