【题目】
LOJ
给定一个字符串
S
S
S,有
Q
Q
Q次询问,每次给出一个字符串
S
l
,
r
S_{l,r}
Sl,r,求有多少对
(
i
,
j
)
(
i
<
j
)
(i,j)(i<j)
(i,j)(i<j)满足将
S
S
S从这里断开后,至少有一段存在
S
l
,
r
S_{l,r}
Sl,r。(有
n
−
1
n-1
n−1个可行的断点)
n
≤
1
0
5
,
Q
≤
3
×
1
0
5
n\leq 10^5,Q\leq 3\times 10^5
n≤105,Q≤3×105
【解题思路】
一开始暴力讨论字符串出现位置后发现做不了,于是考虑求每一段都不出现询问串的方案数,那么当然是先合并出
right
\text{right}
right集合。
先去掉一些特殊情况:
- 第一个断点不切断任何询问串,那么他一定在询问串第一次出现前,第二个断点取到所有询问串的并,这样就是一个乘积。
- 第一个断点切断所有询问串,那么它取到所有询问串的并,第二个断点可以取到第一个断点后所有位置,就是一个类似等差数列求和的东西。
假设一共出现
n
n
n次,剩下要求的东西就是
∑
i
=
1
n
第
1
次
与
第
i
次
出
现
的
交
×
第
i
+
1
次
与
第
n
次
出
现
的
交
\sum_{i=1}^n 第1次与第i次出现的交 \times 第i+1次与第n次出现的交
i=1∑n第1次与第i次出现的交×第i+1次与第n次出现的交
注意这里的交是不包含下一个串覆盖的位置的交才能保证不重不漏。而这个东西在通常情况下就是
l
i
+
1
−
l
i
=
r
i
+
1
−
r
i
l_{i+1}-l_i=r_{i+1}-r_i
li+1−li=ri+1−ri,仅在开头结尾有特例,此时分别对应前两种特殊情况。
那么整理柿子后就可以得到:
a
n
s
=
∑
i
=
2
n
−
1
(
r
i
+
1
−
r
i
)
×
(
r
i
+
1
−
l
n
−
1
+
1
)
=
∑
i
=
2
n
−
1
(
r
i
+
1
−
r
i
)
×
r
i
+
1
−
(
r
i
+
1
−
r
i
)
×
(
l
n
−
1
−
1
)
=
(
∑
i
=
2
n
−
1
(
r
i
+
1
−
r
i
)
×
r
i
+
1
)
−
(
r
n
−
r
2
)
×
(
l
n
−
1
−
1
)
\begin{aligned} ans=&\sum_{i=2}^{n-1} (r_{i+1}-r_i)\times (r_{i+1}-l_{n-1}+1)\\ =&\sum_{i=2}^{n-1} (r_{i+1}-r_i)\times r_{i+1}- (r_{i+1}-r_i)\times (l_{n-1}-1)\\ =&(\sum_{i=2}^{n-1} (r_{i+1}-r_i)\times r_{i+1})- (r_{n}-r_2)\times (l_{n-1}-1) \end{aligned}
ans===i=2∑n−1(ri+1−ri)×(ri+1−ln−1+1)i=2∑n−1(ri+1−ri)×ri+1−(ri+1−ri)×(ln−1−1)(i=2∑n−1(ri+1−ri)×ri+1)−(rn−r2)×(ln−1−1)
求和部分实际上是关于相邻两个位置的信息,我们在线段树上额外维护这个信息即可。
复杂度 O ( ( n + Q ) log n ) O((n+Q)\log n) O((n+Q)logn),细节很多,心态小崩。
【参考代码】
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int N=2e5+10,M=N*22,inf=0x3f3f3f3f;
namespace IO
{
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;
namespace Segment
{
int rt[N];
struct node
{
ll val;int mi,mx;
node(ll _v=0,int _i=0,int _x=0):val(_v),mi(_i),mx(_x){}
node operator +(const node&A){return node(val+A.val+((mx && A.mi)?1ll*A.mi*(A.mi-mx):0),mi?mi:A.mi,A.mx?A.mx:mx);}
};
struct Segment
{
node t[M];
int sz,ls[M],rs[M];
void insert(int &x,int l,int r,int p)
{
x=++sz;t[x]=node(0,p,p);
if(l==r) return;
int mid=(l+r)>>1;
if(p<=mid) insert(ls[x],l,mid,p);
else insert(rs[x],mid+1,r,p);
}
int merge(int x,int y)
{
if(!x || !y) return x+y;
int z=++sz;
ls[z]=merge(ls[x],ls[y]);rs[z]=merge(rs[x],rs[y]);
t[z]=t[ls[z]]+t[rs[z]];
return z;
}
node query(int x,int l,int r,int L,int R)
{
if(!x) return node(0,0,0);
if(L<=l && r<=R) return t[x];
int mid=(l+r)>>1;node res=node(0,0,0);
if(L<=mid) res=res+query(ls[x],l,mid,L,R);
if(R>mid) res=res+query(rs[x],mid+1,r,L,R);
return res;
}
int querymin(int x,int l,int r,int L,int R)
{
if(!x) return inf;
if(L<=l && r<=R) return t[x].mi;
int mid=(l+r)>>1,res=inf;
if(L<=mid) res=min(res,querymin(ls[x],l,mid,L,R));
if(R>mid) res=min(res,querymin(rs[x],mid+1,r,L,R));
return res;
}
int querymax(int x,int l,int r,int L,int R)
{
if(!x) return -inf;
if(L<=l && r<=R) return t[x].mx;
int mid=(l+r)>>1,res=-inf;
if(L<=mid) res=max(res,querymax(ls[x],l,mid,L,R));
if(R>mid) res=max(res,querymax(rs[x],mid+1,r,L,R));
return res;
}
}T;
}
using namespace Segment;
namespace String
{
int f[N][20],pos[N];
vector<int>G[N];
struct SAM
{
int las,sz,fa[N],mx[N],ch[N][10];
SAM(){las=sz=1;}
void extend(int x)
{
int p,np,q,nq;
p=las;las=np=++sz;mx[np]=mx[p]+1;
for(;p && !ch[p][x];p=fa[p]) ch[p][x]=np;
if(!p) fa[np]=1;
else
{
q=ch[p][x];
if(mx[q]==mx[p]+1) fa[np]=q;
else
{
nq=++sz;mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;p && ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}
void dfs(int x)
{
for(int i=1;i<19;++i) f[x][i]=f[f[x][i-1]][i-1];
for(auto v:G[x]) dfs(v),rt[x]=T.merge(rt[x],rt[v]);
}
void build()
{
for(int i=1;i<=sz;++i) G[fa[i]].pb(i),f[i][0]=fa[i];
dfs(1);
}
int get(int l,int r)
{
int len=r-l+1;r=pos[r];
for(int i=18;~i;--i) if(mx[f[r][i]]>=len) r=f[r][i];
return r;
}
}S;
}
using namespace String;
namespace DreamLolita
{
int n,m;
char s[N];
ll calc(int x){return 1ll*x*(x-1)/2;}
void solution()
{
n=read();m=read();scanf("%s",s+1);
for(int i=1;i<=n;++i) S.extend(s[i]^48),pos[i]=S.las,T.insert(rt[S.las],1,n,i);
S.build();
while(m--)
{
int l=read(),r=read(),p=rt[S.get(l,r)],len=r-l+1;ll ans=0;
if(T.t[p].mx-T.t[p].mi<len) ans+=1ll*(len-(T.t[p].mx-T.t[p].mi)-1)*(T.t[p].mi-len+n-T.t[p].mx);
l=max(T.t[p].mi,T.querymax(p,1,n,1,T.t[p].mx-len+1));
r=min(T.t[p].mx,T.querymax(p,1,n,1,T.t[p].mi+len-1));
if(l && r && l<r)
{
node res=T.query(p,1,n,l,r);
ans+=res.val-1ll*(res.mx-res.mi)*(T.t[p].mx-len+1);
}
if(r==T.t[p].mx) ans+=1ll*(T.t[p].mi-(r-len+1))*((len-2)-(floor(T.t[p].mi-(r-len+1)-1)/2));
else ans+=1ll*(T.t[p].mi-(r-len+1))*max(T.querymin(p,1,n,r+1,n)-(T.t[p].mx-len+1),0);
writeln(calc(n-1)-ans);
}
}
}
int main()
{
#ifdef Durant_Lee
freopen("LOJ2479.in","r",stdin);
freopen("LOJ2479.out","w",stdout);
#endif
DreamLolita::solution();
return 0;
}