Description
Solution
- 先考虑\(tp=1\)(即ytp的誊写)。要求在\([1,L-1]∪[R+1,n]\)中出现的本质不同的子串数;那么容斥一下,变成总的本质不同的子串数-每次出现都与\([L,R]\)有交的子串数。前面的问题建一个\(SAM\)可以很好统计;后一个问题,实际上是对于每一个满足\(minr≥L\)的点,统计\(\sum min(R,maxr-len_{fa})-maxr+len\)(其中\(minr\)、\(maxr\)分别表示该点的\(right\)集合中的最小、最大值)。
- 这实际上是个二维数点问题。从后往前扫,扫到一个\(minr\)就在\([maxr-len+1,maxr-len_{fa}]\)上区间+1;扫到一个询问\(L\),就查询\(R\)的前缀和。可以用两个树状数组维护。
- 再考虑\(tp=2\)。这就多了个求子串的本质不同子串数。
(这又是个我完全不造的“经典问题”)
- 考虑先把整个串的\(SAM\)建出来,记录建到每个位置时的结尾点。从左往右激活每个结尾点,激活时相当于更新它到\(fa\)树祖先这整条链的\(maxr\),并且一个点的\(maxr\)会使\([maxr-len+1,maxr-len_{fa}]\)区间+1;对于一个询问\([L,R]\),我们先把\(≤R\)的结尾点激活,然后区间查询\([L,R]\)的和。
- 可以直接套\(LCT\),把\(maxr\)相同的放在同一个\(splay\)里;每次修改时直接\(access\),一路打标记、改树状数组。
Code
#include<cstdio>
#include<vector>
#include<cstring>
#define MIN(x,y) if(x>y)x=y
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=4e5+9;
int n,tp,m,l[N],r[N],d[N];
char s[N];
vector<int>e[N],g[N];
ll c[N],c1[N],ans[N];
void mdf(int k,int d) {if(k<=0)k=1; for(int i=k;i<=n;i+=i&-i) c[i]+=d,c1[i]+=d*k;}
ll qry(int k) {ll a=0; for(int i=k;i;i-=i&-i)a+=(k+1)*c[i]-c1[i]; return a;}
struct SAM_LCT
{
int a[N],u,v,np,cnt=1,ch[N][26],fa[N],Fa[N],len[N],mn[N],mx[N],c[N],_[N],s[N][2],mi[N],tag[N];
ll ans;
void ext(char c,int&nw,int la,int i)
{
len[nw=++cnt]=len[la]+1;
for(u=la;u&&!ch[u][c];u=fa[u]) ch[u][c]=nw;
if(!u) fa[nw]=1; else
if(len[u]+1==len[v=ch[u][c]]) fa[nw]=v; else
{
len[np=++cnt]=len[u]+1, memcpy(ch[np],ch[v],sizeof ch[np]); mn[np]=mx[np]=mn[v];
for(;u&&ch[u][c]==v;u=fa[u]) ch[u][c]=np;
fa[np]=fa[v], fa[v]=fa[nw]=np;
}
mn[nw]=mx[nw]=i;
}
void cal()
{
fo(i,1,cnt) c[len[mi[i]=i]]++, g[mn[i]].push_back(i), ans+=len[i]-len[Fa[i]=fa[i]];
fo(i,1,n) c[i]+=c[i-1];
fo(i,1,cnt) _[c[len[i]]--]=i;
fd(i,cnt,2) if(mx[fa[_[i]]]<mx[_[i]]) mx[fa[_[i]]]=mx[_[i]];
}
bool so(int x) {return s[fa[x]][1]==x;}
bool rt(int x) {return !fa[x]||s[fa[x]][so(x)]!=x;}
void lk(int f,int x,bool k) {if(f)s[f][k]=x; if(x)fa[x]=f;}
void up(int x) {mi[x]=s[x][0]?mi[s[x][0]]:x;}
void rot(int x)
{
if(!x) return;
int y=fa[x],z=fa[y],k=so(x);
if(rt(y)) fa[x]=z; else lk(z,x,so(y));
lk(y,s[x][!k],k); lk(x,y,!k);
up(y); up(x);
}
void push(int x) {if(!tag[x])return; if(s[x][0])tag[s[x][0]]=tag[x]; if(s[x][1])tag[s[x][1]]=tag[x];}
void clr(int x)
{
for(d[d[0]=1]=x;!rt(x);d[++d[0]]=x=fa[x]);
while(d[0]) push(d[d[0]--]);
}
void splay(int x)
{
clr(x);
for(int f=fa[x];!rt(x);rot(x),f=fa[x]) rot(rt(f)?0:so(x)==so(f)?f:x);
}
void access(int y)
{
int x=0,r=mn[y]; mdf(1,1), mdf(r+1,-1);
for(;y;x=y,y=fa[y])
{
int f=y;
while(!rt(f)) f=fa[f];
if(tag[f]) mdf(tag[f]-len[y]+1,-1), mdf(tag[f]-len[Fa[mi[f]]]+1,1);
splay(y); lk(y,x,1); tag[y]=r;
}
}
}S;
int main()
{
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
scanf("%d%d%s%d",&n,&tp,s+1,&m);
S.a[0]=1;
fo(i,1,n) S.ext(s[i]-97,S.a[i],S.a[i-1],i);
S.cal();
fo(i,1,m)
{
scanf("%d%d",l+i,r+i);
e[l[i]].push_back(i);
}
fd(x,n,1)
{
for(int u:g[x]) mdf(S.mx[u]-S.len[u]+1,1), mdf(S.mx[u]-S.len[S.fa[u]]+1,-1);
for(int i:e[x]) ans[i]=S.ans-qry(r[i])+1ll*r[i]*(n-r[i])+(x-1ll)*(r[i]-x+1ll)+(tp==1?r[i]-x+1:0);
}
if(tp==2)
{
memset(c,0,8*(n+1)); memset(c1,0,8*(n+1));
fo(x,1,n) e[x].clear();
fo(i,1,m) e[r[i]].push_back(i);
fo(x,1,n)
{
S.access(S.a[x]);
for(int i:e[x]) ans[i]+=qry(x)-qry(l[i]-1);
}
}
fo(i,1,m) printf("%lld\n",ans[i]);
}