傳送門
題解:
好題啊,除的寫起來傷心情……
首先轉換一下題意,找到這個串的所有出現位置,這個可以用 加倍增實現,然後砍兩刀使得每一個出現位置都被砍斷,下面我們對這兩刀怎麼砍分類討論:
一些規定: 令每個串的出現位置爲 ,出現的次數爲 ,令 爲 , 爲 ,一個點的(不嚴格可以取等)前驅爲它前面第一個出現的串的 即 ,一個點的後繼(可以取等)爲後面第一個 ,符號分別爲 ,本次詢問的覆蓋範圍爲 ,本次詢問串的長度爲 ,在 切一刀指的是將 切斷
-
的前驅的左端點與 不相交,或者只交了一個字符,這種情況顯然方案爲 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;
}