传送门
题解:
好题啊,除的写起来伤心情……
首先转换一下题意,找到这个串的所有出现位置,这个可以用
加倍增实现,然后砍两刀使得每一个出现位置都被砍断,下面我们对这两刀怎么砍分类讨论:
一些规定: 令每个串的出现位置为 ,出现的次数为 ,令 为 , 为 ,一个点的(不严格可以取等)前驱为它前面第一个出现的串的 即 ,一个点的后继(可以取等)为后面第一个 ,符号分别为 ,本次询问的覆盖范围为 ,本次询问串的长度为 ,在 切一刀指的是将 切断
-
的前驱的左端点与 不相交,或者只交了一个字符,这种情况显然方案为 0 -
,这种情况中可以分为几类:第一刀没切完第二刀把剩下的切完,第一刀切完,第二刀也切完或切第一刀左边的一部分或随便切一个地方( )
发现这个贡献可以分段统计,枚举第一刀的端点 ,那么可行的第二刀可以切在
这个的方案数是 ,第一刀切在中间,需要加上不同于刚刚那种情况的答案,分都在中间还是只有第一刀在中间讨论,那么可以得到方案数为
那么我们可以考虑用线段树维护 -
,这种情况不存在一刀端,必须两刀配合完成,同样考虑当前选择
那么需要满足 ,同时 ,于是我们可以在线段树上完成这样一个区间查询
假设最后一个合法的区间为 ( ),那么 是可以切的,找到这个 可以通过查 的前驱和 后继来实现,贡献为
同时需要找到第一个满足 的区间最为起始节点,这个可以通过 的前缀找到
然后就做完啦,个人感觉讨论得还比较清楚
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
typedef long long ll;
cs int N = 3e5 + 50;
cs int INF = 1e9;
int n, m, ps[N], rt[N]; ll ans[N];
char S[N];
struct query{
int l, r, c;
query(int _l=0, int _r=0, int _c=0){ l=_l; r=_r; c=_c; }
};
vector<query> qry[N];
vector<int> G[N]; int fa[N][20];
namespace SGT{
cs int N = ::N * 40;
int nd,ls[N],rs[N],mi[N],mx[N]; ll v1[N],v2[N];
#define mid ((l+r)>>1)
void pushup(int x){
if(!ls[x]){ mi[x]=mi[rs[x]]; mx[x]=mx[rs[x]]; v1[x]=v1[rs[x]]; v2[x]=v2[rs[x]]; return; }
if(!rs[x]){ mi[x]=mi[ls[x]]; mx[x]=mx[ls[x]]; v1[x]=v1[ls[x]]; v2[x]=v2[ls[x]]; return; }
mi[x]=mi[ls[x]]; mx[x]=mx[rs[x]];
int delta = mi[rs[x]]-mx[ls[x]];
v1[x]=v1[ls[x]]+v1[rs[x]]+(ll)delta*mi[rs[x]];
v2[x]=v2[ls[x]]+v2[rs[x]]+delta;
}
void ins(int &x, int l, int r, int p){
if(!x) x=++nd; mi[x]=mx[x]=p; if(l==r) return;
(p<=mid)?ins(ls[x],l,mid,p):ins(rs[x],mid+1,r,p);
}
void merge(int &x, int y){
if(!x||!y){ x|=y; return; }
merge(ls[x],ls[y]); merge(rs[x],rs[y]); pushup(x);
}
int qrymi(int x, int l, int r, int L, int R){
if(!x) return INF;
if(L<=l&&r<=R) return mi[x]; int as=INF;
if(L<=mid) as=min(as,qrymi(ls[x],l,mid,L,R)); if(as!=INF) return as;
if(R>mid) as=min(as,qrymi(rs[x],mid+1,r,L,R)); return as;
}
int qrymx(int x, int l, int r, int L, int R){
if(!x) return -INF;
if(L<=l&&r<=R) return mx[x]; int as=-INF;
if(R>mid) as=max(as,qrymx(rs[x],mid+1,r,L,R)); if(as!=-INF) return as;
if(L<=mid) as=max(as,qrymx(ls[x],l,mid,L,R)); return as;
}
ll as1, as2, trans;
void query(int x, int l, int r, int L, int R){
if(!x) return;
if(L<=l && r<=R){
ll delta = mi[x] - trans; trans = mx[x];
as1 += v1[x] + delta * mi[x];
as2 += v2[x] + delta; return;
}
if(L<=mid) query(ls[x],l,mid,L,R);
if(R>mid) query(rs[x],mid+1,r,L,R);
}
ll qry(int x, int l, int r, int ps, int R){
trans=ps; as1=as2=0; query(x,1,n,l,r);
return as1-as2*R;
}
ll calc(int x, int R){ return v1[x]-(ll)v2[x]*R; }
}
namespace SAM{
int ch[N][10],lk[N],len[N],r[N],nd=1,las=1;
int extend(int k, int c){
int p=las, now=++nd; len[now]=len[las]+1; r[now]=k;
for(;p&&!ch[p][c];p=lk[p]) ch[p][c]=now;
if(!p) lk[now] = 1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1) lk[now]=q;
else{
int cl=++nd; len[cl]=len[p]+1; lk[cl]=lk[q];
memcpy(ch[cl],ch[q],sizeof(ch[q]));
lk[q]=lk[now]=cl;
for(;p&&ch[p][c]==q;p=lk[p]) ch[p][c]=cl;
}
} las = now; return now;
}
void ready(){
for(int i=1; i<=nd; i++) G[lk[i]].pb(i);
for(int i=1; i<=nd; i++) if(r[i]) SGT::ins(rt[i],1,n,r[i]);
}
}
void pre_dfs(int u){
for(int i=1; i<=18; i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(int v : G[u]) fa[v][0]=u, pre_dfs(v);
}
int jump(int l, int r){
int nx = ps[r], len = r-l+1;
for(int i=18; ~i; i--) if(SAM::len[fa[nx][i]]>=len) nx=fa[nx][i];
return nx;
}
ll C2(int x){ return (ll)x*(x-1)/2;}
void work(int u){
for(int v : G[u]){
work(v);
SGT::merge(rt[u],rt[v]);
}
int L = SGT::mi[rt[u]], ed = SGT::mx[rt[u]];
for(auto t : qry[u]){
int len = t.r - t.l + 1, R = ed - len + 1;
if(R<=L) ans[t.c] = SGT::calc(rt[u],R) + C2(L-R) + (ll)(L-R)*(n-len);
else{
int pos = SGT::qrymx(rt[u],1,n,1,R);
if(L+len-1<=pos) continue;
ans[t.c] = SGT::qry(rt[u],R+1,L+len-1,pos,R);
int rp = SGT::qrymi(rt[u],1,n,L+len,n), lp = SGT::qrymx(rt[u],1,n,1,L+len-1);
if(rp!=INF&&lp!=-INF) if(rp>R) ans[t.c]+=(ll)(rp-R)*(L-(lp-len+1));
}
}
}
int main(){
n = read(), m = read();
scanf("%s",S+1);
for(int i=1; i<=n; i++) ps[i]=SAM::extend(i,S[i]-'0');
SAM::ready(); pre_dfs(0);
for(int i=1; i<=m; i++){
int l=read(), r=read(), nx=jump(l,r);
qry[nx].push_back(query(l,r,i));
}
work(0); ll sm = C2(n-1);
for(int i=1; i<=m; i++) cout << sm-ans[i] << '\n';
return 0;
}