先考虑对一个串的子串求不同子串的个数怎么做,
先对询问离线,
考虑不断右移右端点,同时维护左端点为1~i的答案,
显然对于一种相同的子串,只计算最右边的,
在SAM中,一个节点代表了一些子串,而这些子串Right的最大值,也就是Fail树上这个点的所有儿子的Right最大值,
所以每次加入一个点,相当于更改一条链上点的Right最大值,
我发现这个就是LCT上的Access操作,
所以做Access时维护区间加减即可,
复杂度: O ( n log 2 ( n ) ) O(n\log^2(n)) O(nlog2(n))
#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
#define NX(q) ((q)&(-(q)))
#define SD(q) (b[b[q].fa].r==(q))
using namespace std;
typedef long long LL;
const int N=600500;
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
int a[N],anx[N];
int ASK[N][3];
LL Ans[N];
int B[N][2],A[N],B0;
int zx[N];
struct SAMT
{
int a[29];
int fail,rx,ti;
}b[N];
int b0=1;
int p[N];
bool PX(int q,int w){return b[q].ti<b[w].ti||(b[q].ti==b[w].ti&&b[q].rx<b[w].rx);}
LL sum[N][2];
void ADD(int q,int e){for(int e1=e*(n-q+1LL);q<=n;q+=NX(q))sum[q][0]+=e,sum[q][1]+=e1;}
LL FD(int Q)
{
LL ans=0,ans1=0;
for(int q=Q;q;q-=NX(q))ans+=sum[q][0],ans1+=sum[q][1];
return ans1-(n-Q)*ans;
}
void change(int l1,int r1,int l2){ADD(l1,l2),ADD(r1+1,-l2);}
LL find(int l1,int r1){return FD(r1)-FD(l1-1);}
struct LCTv
{
struct qqww
{
int l,r,fa,Fa,si;
int miv,mxv,rx;
int mi,mx;
}b[N];
int b0;
void doit(int q)
{
b[q].si=b[b[q].l].si+b[b[q].r].si+1;
b[q].mi=min(b[q].miv,min(b[b[q].l].mi,b[b[q].r].mi));
b[q].mx=max(b[q].mxv,max(b[b[q].l].mx,b[b[q].r].mx));
}
void rotate(int q)
{
int w=b[q].fa;
swap(b[q].Fa,b[w].Fa);
swap(b[q].rx,b[w].rx);
if(SD(q))
{
b[w].r=b[q].l;
b[b[q].l].fa=w;
b[q].l=w;
}else
{
b[w].l=b[q].r;
b[b[q].r].fa=w;
b[q].r=w;
}
if(SD(w))b[b[w].fa].r=q;
else b[b[w].fa].l=q;
b[q].fa=b[w].fa;b[w].fa=q;
doit(w),doit(q);
}
void Splay(int q,int T)
{
for(;b[q].fa!=T;)
{
if(b[b[q].fa].fa!=T)
{
if(SD(q)==SD(b[q].fa))rotate(b[q].fa);
else rotate(q);
}
rotate(q);
}
}
void access(int q,int rx)
{
int w=q;
for(q=b[q].Fa;q;w=q,q=b[q].Fa)
{
Splay(q,0);
b[b[q].r].rx=b[q].rx;
b[b[q].r].fa=0,b[b[q].r].Fa=q;
b[q].r=0;doit(q);
change(b[q].rx-b[q].mx+1,b[q].rx-(b[q].mi)+1,-1);
// change(b[q].rx-b[q].mx+1,b[q].rx-b[q].mi+1,-1);
b[q].rx=rx;b[w].rx=0;
b[q].r=w;b[w].fa=q;b[w].Fa=0;doit(q);
}
change(rx-b[w].mx+1,rx-b[w].mi+1,1);
}
int Doit(int mi,int mx,int rx,int fa)
{
mi=min(mx,mi);
b[++b0].miv=mi;b[b0].mxv=mx;
b[b0].Fa=fa;b[b0].rx=rx;
doit(b0);
access(b0,rx);
return b0;
}
}LCT;
void link(int q,int w){B[++B0][0]=A[q];A[q]=B0,B[B0][1]=w;}
int nw=1;
void Join_SAM(int c)
{
++b0;
b[nw].a[c]=b0;
b[b0].rx=b[nw].rx+1;
b[b0].ti=b[nw].ti+1;
int q,p;
for(q=b[nw].fail;q&&!b[q].a[c];q=b[q].fail)b[q].a[c]=b0;
nw=b0;
p=b[q].a[c];
if(!q)b[b0].fail=1;
else if(b[p].rx==b[q].rx+1)b[b0].fail=p;
else
{
++b0;
b[b0].ti=b[p].ti;
b[b0].fail=b[p].fail;
b[nw].fail=b[p].fail=b0;
b[b0].rx=b[q].rx+1;
fo(i,0,25)b[b0].a[i]=b[p].a[i];
for(;q&&b[q].a[c]==p;q=b[q].fail)b[q].a[c]=b0;
}
}
void Doit(int m)
{
fo(i,1,b0)p[i]=i;
sort(p+1,p+1+b0,PX);
fo(i,1,m)link(ASK[i][1],i);
int L=2;
LCT.b[0].mi=1e9;
fo(i,1,n)
{
for(;L<=b0&&b[p[L]].ti<=i;++L)
{
int q=p[L];
// printf("%d\n",q);
zx[q]=LCT.Doit(b[b[q].fail].rx+1,b[q].rx,b[q].ti,zx[b[q].fail]);
}
efo(j,i)
{
Ans[B[j][1]]=FD(i)-FD(ASK[B[j][1]][0]-1);
}
}
}
int main()
{
freopen("zifuchuan.in","r",stdin);
freopen("zifuchuan.out","w",stdout);
int q,w;
char ch=getchar();
for(;ch>='a'&&ch<='z';ch=getchar())a[++n]=ch-'a'+1;
q=0;
fo(i,2,n)
{
for(;q&&a[q+1]!=a[i];q=anx[q]);
if(a[q+1]==a[i])++q;
anx[i]=q;
}
w=n-anx[n];
int la=anx[n];
for(q=anx[n];q;q=anx[q])
{
if(la-anx[q]>w)
{
w=0;
break;
}
else if(la-anx[q]==w)la=anx[q];
}
if(w&&!la)n=w;
int n1=n;
fo(i,n+1,n*3)a[i]=a[i-n];n*=3;
fo(i,1,n)Join_SAM(a[i]-1);
read(m);
fo(i,1,m)
{
read(q),read(w);
if(q>n1)
{
int t=(q-1)/n1;
q-=t*n1,w-=t*n1;
}
ASK[i][0]=q,ASK[i][2]=w;
if(w<=n)ASK[i][1]=w;
else ASK[i][1]=q+n1*2-1;
}
Doit(m);
fo(i,1,m)
{
Ans[i]+=(LL)(n1)*(ASK[i][2]-ASK[i][1]);
printf("%lld\n",Ans[i]);
}
return 0;
}