这题细节好多阿orz好毒瘤阿orz
首先我们对于每一个询问子串,如果处理出了他的所有出现位置,那么我们反过来求,就是要求用两个断点把所有线段都断开的方案数。
处理出所有询问子串的出现位置可以SAM+parent树上倍增来快速定位子串所在节点,这个节点的Right集合就是所有的出现位置。我们可以用线段树合并来维护这个Right集合。复杂度 O((Q+n)logn) O ( ( Q + n ) l o g n )
然后我们考虑用两个断点把所有线段都断开的方案数怎么求。
首先如果存在三条及以上互不相交的线段则肯定无解。
那么至多只有第一条和最后一条线段不相交。
我们考虑第一个断点放在li~li+1之间时,第二个断点必须放在ln~ri+1之间。
所以总方案数应该是
∑i(li+1−li)∗(ri+1−ln)
∑
i
(
l
i
+
1
−
l
i
)
∗
(
r
i
+
1
−
l
n
)
因为长度都相等,因此也就是
∑i(ri+1−ri)∗(ri+1−ln)
∑
i
(
r
i
+
1
−
r
i
)
∗
(
r
i
+
1
−
l
n
)
也就是
∑i(ri+1−ri)∗ri+1−(ri+1−ri)∗ln)
∑
i
(
r
i
+
1
−
r
i
)
∗
r
i
+
1
−
(
r
i
+
1
−
r
i
)
∗
l
n
)
我们分别维护一下
(ri+1−ri)∗ri+1
(
r
i
+
1
−
r
i
)
∗
r
i
+
1
和
(ri+1−ri)
(
r
i
+
1
−
r
i
)
即可。
还要注意很多的细节。。。比如li~li+1一定要和第一条线段有交,ri+1要>ln等等。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100010
#define inf 0x3f3f3f3f
#define pa pair<int,int>
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,Q,m=0,par[N<<1],son[N<<1][10],fa[N<<1][20],Log[N<<1],lst[N],mx[N<<1],owo=0,Rt=0;
int rt[N<<1];char s[N];
ll ans[N*3];
vector<int>Son[N<<1];vector<pa> qu[N<<1];
struct node{
int lc,rc,mn,mx,sum1,sz;
ll sum2;
}tr[N*20];
inline void pushup(int p){
int l=tr[p].lc,r=tr[p].rc;
tr[p].sz=tr[l].sz+tr[r].sz;
tr[p].mn= l?tr[l].mn:tr[r].mn;
tr[p].mx= r?tr[r].mx:tr[l].mx;
tr[p].sum1=tr[p].mx-tr[p].mn;
tr[p].sum2=tr[l].sum2+tr[r].sum2;
if(l&&r) tr[p].sum2+=((ll)tr[r].mn-tr[l].mx)*tr[r].mn;
}
inline void ins(int &p,int l,int r,int x){
p=++owo;tr[p].mn=tr[p].mx=x;tr[p].sz=1;
if(l==r) return;int mid=l+r>>1;
if(x<=mid) ins(tr[p].lc,l,mid,x);
else ins(tr[p].rc,mid+1,r,x);
}
inline void merge(int &p1,int p2){
if(!p1){p1=p2;return;}
if(!p2) return;
merge(tr[p1].lc,tr[p2].lc);
merge(tr[p1].rc,tr[p2].rc);pushup(p1);
}
inline void qsucc(int p,int l,int r,int x,int &pos,int &rx){//查x的后继
if(l==r){rx=l;return;}
int mid=l+r>>1;
if(tr[tr[p].lc].mx>x) pos-=tr[tr[p].rc].sz,qsucc(tr[p].lc,l,mid,x,pos,rx);
else qsucc(tr[p].rc,mid+1,r,x,pos,rx);
}
inline void qpre(int p,int l,int r,int x,int &pos,int &rx){//查x的前驱
if(l==r){rx=l;return;}
int mid=l+r>>1;
if(tr[tr[p].rc].mn<x) pos+=tr[tr[p].lc].sz,qpre(tr[p].rc,mid+1,r,x,pos,rx);
else qpre(tr[p].lc,l,mid,x,pos,rx);
}
inline int qkth(int p,int l,int r,int x){
if(l==r) return l;
int mid=l+r>>1;
if(x<=tr[tr[p].lc].sz) return qkth(tr[p].lc,l,mid,x);
return qkth(tr[p].rc,mid+1,r,x-tr[tr[p].lc].sz);
}
inline void query(int p,int l,int r,int x,int y,int &sum1,ll &sum2){
if(!p) return;
if(x==l&&r==y){sum1+=tr[p].sum1;sum2+=tr[p].sum2;return;}
int mid=l+r>>1;
if(y<=mid) query(tr[p].lc,l,mid,x,y,sum1,sum2);
else if(x>mid) query(tr[p].rc,mid+1,r,x,y,sum1,sum2);
else{
query(tr[p].lc,l,mid,x,mid,sum1,sum2);
query(tr[p].rc,mid+1,r,mid+1,y,sum1,sum2);
int pl=tr[p].lc,pr=tr[p].rc;
if(pl&&pr) sum1+=tr[pr].mn-tr[pl].mx,sum2+=((ll)tr[pr].mn-tr[pl].mx)*tr[pr].mn;
}
}
inline void extend(int ch,int x){
int p=lst[x-1],np=++m;mx[np]=mx[p]+1;lst[x]=np;
ins(rt[np],1,n,x);
for(;!son[p][ch];p=par[p]) son[p][ch]=np;
if(!p){par[np]=Rt;return;}
int q=son[p][ch];
if(mx[q]==mx[p]+1){par[np]=q;return;}
int nq=++m;memcpy(son[nq],son[q],sizeof(son[q]));mx[nq]=mx[p]+1;
par[nq]=par[q];par[q]=par[np]=nq;
for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;
}
inline void dfs1(int x){
for(int i=1;i<=Log[m];++i){
if(!fa[x][i-1]) break;
fa[x][i]=fa[fa[x][i-1]][i-1];
}for(int i=0;i<Son[x].size();++i){
int y=Son[x][i];if(y==fa[x][0]) continue;
fa[y][0]=x;dfs1(y);
}
}
inline int getx(int x,int l){
for(int i=Log[m];i>=0;--i)
if(mx[fa[x][i]]>=l) x=fa[x][i];
return x;
}
inline ll S(int l,int r){return ((ll)l+r)*(r-l+1)>>1;}
inline void dfs(int x){
for(int i=0;i<Son[x].size();++i){
int y=Son[x][i];if(y==fa[x][0]) continue;dfs(y);
merge(rt[x],rt[y]);
}for(int i=0;i<qu[x].size();++i){
int id=qu[x][i].second,len=qu[x][i].first,r1=tr[rt[x]].mn,ln=tr[rt[x]].mx-len+1;
if(tr[rt[x]].sz==1){ans[id]=((ll)ln-1)*(len-1)+S(n-r1,n-ln-1);continue;}
int pos1=tr[rt[x]].sz,pos2=1,rx,ry;qsucc(rt[x],1,n,ln,pos1,rx);qpre(rt[x],1,n,r1+len-1,pos2,ry);
int lx=rx-len+1,ly=ry-len+1;
//pos1--第一个和最后一条直线有交的,pos2--最后一个和第一条直线有交的。
if(pos1>pos2+1) continue;
if(pos1==pos2+1){ans[id]=((ll)r1-ly)*(rx-ln);continue;}
if(pos1==1){
ans[id]=(ll)(r1-len)*(r1-ln)+S(n-r1,n-ln-1);
ans[id]+=tr[rt[x]].sum2-(ll)tr[rt[x]].sum1*ln;continue;
}ans[id]=((ll)r1-ly)*(qkth(rt[x],1,n,pos2+1)-ln);
int sum1=0;ll sum2=0;query(rt[x],1,n,qkth(rt[x],1,n,pos1-1),ry,sum1,sum2);
ans[id]+=sum2-(ll)sum1*ln;
}
}
int main(){
// freopen("a.in","r",stdin);
n=read();Q=read();Rt=lst[0]=++m;tr[0].mx=-inf;tr[0].mn=inf;
scanf("%s",s+1);Log[0]=-1;
for(int i=1;i<=n;++i) extend(s[i]-'0',i);
for(int i=1;i<=m;++i) Son[par[i]].push_back(i),Log[i]=Log[i>>1]+1;
dfs1(Rt);for(int i=1;i<=Q;++i){
int l=read(),r=read(),x=getx(lst[r],r-l+1);
qu[x].push_back(make_pair(r-l+1,i));
}dfs(Rt);ll ALL=S(1,n-2);
for(int i=1;i<=Q;++i) printf("%lld\n",ALL-ans[i]);
return 0;
}