题意
给一个长度为n的01串,每次询问给出一个区间[l,r],问所有结束位置在[l,r]中的前缀,两两lcs的最大值是多少。
n,m≤105
n
,
m
≤
10
5
分析
显然一个前缀对(x,y)能贡献到所有满足
l≤x,y≤r
l
≤
x
,
y
≤
r
的询问
[l,r]
[
l
,
r
]
。
两个前缀的lcs等价于其在sam的parents树上lca的mx。那么我们可以先把sam建出来,用set维护当前点的right集,然后从底往上启发式合并。
每次合并两个set的时候,把所有新加入的相邻点对取出来,权值是当前点的mx,那么就只有
O(nlogn)
O
(
n
l
o
g
n
)
个点对会造成贡献。
剩下的就是一个二维数点问题,用扫描线+树状数组来搞就好了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>
const int N=100005;
int n,m,sz,ls,b[N*2],c[N*2],t[N],ch[N*2][2],mx[N*2],tot,fa[N*2],ans[N];
char str[N];
struct data{int x,y,id;}a[N*30],q[N];
std::set<int> se[N*2];
std::set<int>::iterator it,it1;
void extend(int x)
{
int p,q,np,nq;
p=ls;ls=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 (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}
void pre()
{
for (int i=1;i<=sz;i++) b[mx[i]]++;
for (int i=1;i<=sz;i++) b[i]+=b[i-1];
for (int i=sz;i>=1;i--) c[b[mx[i]]--]=i;
for (int i=sz;i>=2;i--)
{
int x=c[i],y=fa[x];
if (se[x].size()>se[y].size()) std::swap(se[x],se[y]);
for (it=se[x].begin();it!=se[x].end();it++)
{
int w=*it;
it1=se[y].lower_bound(w);
if (it1!=se[y].end()) a[++tot]=(data){w,*it1,mx[y]};
if (it1!=se[y].begin()) it1--,a[++tot]=(data){*it1,w,mx[y]};
}
for (it=se[x].begin();it!=se[x].end();it++) se[y].insert(*it);
}
}
bool cmp(data a,data b)
{
return a.x>b.x;
}
void ins(int x,int y)
{
while (x<=n) t[x]=std::max(t[x],y),x+=x&(-x);
}
int query(int x)
{
int ans=0;
while (x) ans=std::max(ans,t[x]),x-=x&(-x);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",str+1);
ls=sz=1;
for (int i=1;i<=n;i++) extend(str[i]-'0'),se[ls].insert(i);
pre();
std::sort(a+1,a+tot+1,cmp);
for (int i=1;i<=m;i++) scanf("%d%d",&q[i].x,&q[i].y),q[i].id=i;
std::sort(q+1,q+m+1,cmp);
int p=1;
for (int i=1;i<=m;i++)
{
while (p<=tot&&a[p].x>=q[i].x) ins(a[p].y,a[p].id),p++;
ans[q[i].id]=query(q[i].y);
}
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}