【洛谷 P4384】 [八省联考2018]制胡窜(SAM / 线段树合并)
首先建出,线段树合并维护
显然考虑问题可以变成
给定条线段
选择两个点切下去,存在某条线段未被切开的方案
首先转化成求所有线段都被切开的方案
用减去即可
设
然后分几种情况考虑:
1、存在线段
那此时答案一定为0
2、
那么此时考虑两种情况
(1):要么两个点分别切开一些线段
(2):要么一个点切开所有线段,另一个点随便放
(1):
考虑一个放在内,那另一个必须在
那么这个的方案就是
(2):
注意不要与(1)中的重了
所以此时的方案为
3、即,且所有其他线段与线段或相连
仍然考虑类似2(1)的做法
那么可以得到方案为
注意此时需要满足
考虑还有就是最后一个合法区间
设分别为的前面和后面第一个
那么贡献就还有
用线段树合并的时候维护一下
具体实现可以看代码
#include<bits/stdc++.h>
using namespace std;
#define cs const
#define re register
#define pb push_back
#define pii pair<int,int>
#define ll long long
#define fi first
#define se second
#define bg begin
cs int RLEN=1<<20|1;
inline char gc(){
static char ibuf[RLEN],*ib,*ob;
(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ib==ob)?EOF:*ib++;
}
inline int read(){
char ch=gc();
int res=0;bool f=1;
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
return f?res:-res;
}
inline void readstring(char *s){
int top=0;char ch=gc();
while(isspace(ch))ch=gc();
while(!isspace(ch)&&ch!=EOF)s[++top]=ch,ch=gc();
}
template<class tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<class tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=100005;
int n,q;
namespace Seg{
cs int N=::N*70;
int mn[N],mx[N],tot,lc[N],rc[N],last;
ll s1[N],s2[N],tp1,tp2;
#define mid ((l+r)>>1)
inline int copy(int r1){
int u=++tot;mn[u]=mn[r1],mx[u]=mx[r1],lc[u]=lc[r1],rc[u]=rc[r1],s1[u]=s1[r1],s2[u]=s2[r1];
return u;
}
inline void pushup(int u){
if(!lc[u])mn[u]=mn[rc[u]],mx[u]=mx[rc[u]];
else if(!rc[u])mn[u]=mn[lc[u]],mx[u]=mx[lc[u]];
else mn[u]=mn[lc[u]],mx[u]=mx[rc[u]];
s1[u]=s1[lc[u]]+s1[rc[u]],s2[u]=s2[lc[u]]+s2[rc[u]];
if(!lc[u]||!rc[u])return;
int del=mn[rc[u]]-mx[lc[u]];
s1[u]+=del*mn[rc[u]],s2[u]+=del;
}
void insert(int &u,int l,int r,int p){
u=copy(u);if(l==r){mx[u]=mn[u]=p;return;}
if(p<=mid)insert(lc[u],l,mid,p);else insert(rc[u],mid+1,r,p);
pushup(u);
}
void merge(int &u,int v){
if(!u&&!v)return;
if(!u||!v){u=copy(u+v);return;}
u=copy(u);
merge(lc[u],lc[v]),merge(rc[u],rc[v]);
pushup(u);
}
int querymn(int u,int l,int r,int st,int des){
if(!u||st>des)return 1e9;
if(st<=l&&r<=des)return mn[u];
int ret=1e9;
if(st<=mid)chemn(ret,querymn(lc[u],l,mid,st,des));
if(ret<1e9)return ret;
if(mid<des)chemn(ret,querymn(rc[u],mid+1,r,st,des));
return ret;
}
int querymx(int u,int l,int r,int st,int des){
if(!u||st>des)return -1e9;
if(st<=l&&r<=des)return mx[u];
int ret=-1e9;
if(mid<des)chemx(ret,querymx(rc[u],mid+1,r,st,des));
if(ret>-1e9)return ret;
if(st<=mid)chemx(ret,querymx(lc[u],l,mid,st,des));
return ret;
}
void query(int u,int l,int r,int st,int des){
if(!u)return;
if(st<=l&&r<=des){
int del=mn[u]-last;last=mx[u];
tp1+=s1[u]+1ll*del*mn[u];
tp2+=s2[u]+del;
return;
}
if(st<=mid)query(lc[u],l,mid,st,des);
if(mid<des)query(rc[u],mid+1,r,st,des);
}
inline int ask(int u,int l,int r,int fir,int k){
last=fir,tp1=tp2=0,query(u,1,n,l,r);
return tp1-tp2*k;
}
inline int calc(int u,int k){
return s1[u]-s2[u]*k;
}
#undef mid
}
inline ll C2(int x){return 1ll*x*(x-1)/2;}
namespace Sam{
cs int N=::N<<1;
int f[18][N],last=1,tot=1;
int fa[N],nxt[N][10],len[N],pos[N],ps[N];
inline void insert(int c,int id){
int cur=++tot,p=last;last=cur;
len[cur]=len[p]+1,pos[id]=cur,ps[cur]=id;
for(;p&&!nxt[p][c];p=fa[p])nxt[p][c]=cur;
if(!p)fa[cur]=1;
else{
int q=nxt[p][c];
if(len[p]+1==len[q])fa[cur]=q;
else{
int clo=++tot;
fa[clo]=fa[q],len[clo]=len[p]+1;
memcpy(nxt[clo],nxt[q],sizeof(nxt[q]));
for(;p&&nxt[p][c]==q;p=fa[p])nxt[p][c]=clo;
fa[cur]=fa[q]=clo;
}
}
}
int buc[N],rk[N],rt[N];
inline void build(){
for(int i=1;i<=tot;i++)buc[len[i]]++,f[0][i]=fa[i];
for(int i=1;i<=tot;i++)buc[i]+=buc[i-1];
for(int i=1;i<=tot;i++)rk[buc[len[i]]--]=i;
for(int i=1;i<=17;i++)
for(int j=1;j<=tot;j++)f[i][j]=f[i-1][f[i-1][j]];
for(int i=tot;i>=1;i--){
int u=rk[i];
if(ps[u])Seg::insert(rt[u],1,n,ps[u]);
Seg::merge(rt[fa[u]],rt[u]);
}
}
inline int jump(int u,int k){
for(int i=17;~i;i--){
if(len[f[i][u]]>=k)u=f[i][u];
}
return u;
}
inline ll query(int l,int r){
int u=pos[r],len=r-l+1;u=jump(u,len);
int L=Seg::mn[rt[u]],R=Seg::mx[rt[u]]-len+1;
if(R<=L)return C2(n-1)-(Seg::calc(rt[u],R)+C2(L-R)+1ll*(L-R)*(n-len));
int pre=Seg::querymx(rt[u],1,n,1,R),suf;
if(pre-len+1>=L)return C2(n-1);
ll res=Seg::ask(rt[u],R+1,L+len-1,pre,R);
pre=Seg::querymx(rt[u],1,n,1,L+len-1),suf=Seg::querymn(rt[u],1,n,L+len,n);
if(pre!=1e9&&suf!=-1e9&&suf>R)res+=1ll*(suf-R)*(L-pre+len-1);
return C2(n-1)-res;
}
}
char s[N];
int main(){
#ifdef Stargazer
freopen("lx.in","r",stdin);
#endif
n=read(),q=read();
readstring(s);
for(int i=1;i<=n;i++)Sam::insert(s[i]-'0',i);
Sam::build();
while(q--){
int l=read(),r=read();
cout<<Sam::query(l,r)<<'\n';
}
return 0;
}