[洛谷P4482] BJWC2018 Border的四种求法
题目链接
解析
考虑转化一下问题,即对每个询问求最大的 \(i\in[l,r)\) 满足 \(i-l+1\le \text{LCS}(i,r)\) 。
我们先建一棵后缀树,并用线段树合并维护每个节点的 \(\text{endpos}\) 集合。后缀树有这样一个性质:两个字符串的 \(\text{LCS}\) 就是这两个字符串对应的点在后缀树上的 \(\text{LCA}\) 的 \(len\) 值。因此,对于每一个询问,先确定 \(r\) 代表的前缀所在的点,然后暴力向上跳父亲。每经过一个点就在它的线段树中查询最大的满足要求的 \(i\) ,这就是一个线段树区间最大值问题。
现在用树链剖分优化一下上面的过程。每个点到根节点的路径可以拆成若干条重链的前缀,所以我们把每一个询问都拆到这若干个前缀的最后一个点上。对于一条重链,然后从上往下依次枚举重链上的点。考虑如何统计答案:当前点 \(x\) 是其所有轻子树中的点与询问点的 \(\text{LCA}\) ,也就是说轻子树中的点与前缀 \(S_r\) 的 \(\text{LCP}\) 是相同的。因此,我们设 \(val_i=i-len_x\),我们要求的就是满足 \(val_i<l\) 且 \(i\in [l,r]\) 的最大的 \(i\) 。因此,我们每到一个节点,就把这个节点的所有轻子树的 \(val\) 加入一个线段树中,那么挂在这个点上的询问的答案由两部分组成:在上面所说的线段树中二分得到的 \(i\) 和这个 \(i\) 的 \(\text{endpos}\) 集合中最大的满足要求的 \(i\) 。
具体实现时,可以先处理与当前重链相关的所有重链,然后在按照上面的过程。每个点加入线段树的次数不会超过\(\log\) 次,因此复杂度是 \(O(n\log ^2n)\) 。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define N 400002
using namespace std;
struct query{
int l,r,id;
query(){}
query(int _l,int _r,int _id){
l=_l;r=_r;id=_id;
}
}a[N];
char s[N];
int head[N],ver[N],nxt[N],l;
int n,m,i,size[N],son[N],fa[N],dep[N],top[N],ans[N];
vector<query> v[N];
struct SAM{
struct node{
int len,link,son[26];
}t[N*2];
int pos[N],id[N*2],cnt,last;
void extend(char s,int i){
int cur=++cnt,now=last,x=(int)(s-'a');
t[cur].len=t[now].len+1;
id[cur]=i;pos[i]=cur;
while(now&&!t[now].son[x]){
t[now].son[x]=cur;
now=t[now].link;
}
if(now==0) t[cur].link=1;
else{
int p=now,q=t[p].son[x];
if(t[q].len==t[p].len+1) t[cur].link=q;
else{
int p=now,q=t[p].son[x];
if(t[q].len==t[p].len+1) t[cur].link=q;
else{
int tmp=++cnt;
t[tmp]=t[q];
t[tmp].len=t[p].len+1;
t[q].link=t[cur].link=tmp;
while(p&&t[p].son[x]==q){
t[p].son[x]=tmp;
p=t[p].link;
}
}
}
}
last=cur;
}
}P;
struct SegmentTree1{
struct node{
int l,r,dat;
}t[N*20];
int cnt,root[N];
void change(int &p,int l,int r,int x,int v){
if(!p) p=++cnt;
if(l==r){
t[p].dat=v;
return;
}
int mid=(l+r)/2;
if(x<=mid) change(t[p].l,l,mid,x,v);
else change(t[p].r,mid+1,r,x,v);
t[p].dat=max(t[t[p].l].dat,t[t[p].r].dat);
}
int ask(int p,int l,int r,int ql,int qr){
if(!p) return 0;
if(ql<=l&&r<=qr) return t[p].dat;
int mid=(l+r)/2,ans=0;
if(ql<=mid) ans=max(ans,ask(t[p].l,l,mid,ql,qr));
if(qr>mid) ans=max(ans,ask(t[p].r,mid+1,r,ql,qr));
return ans;
}
int merge(int x,int y){
if(x==0) return y;
if(y==0) return x;
t[x].l=merge(t[x].l,t[y].l);
t[x].r=merge(t[x].r,t[y].r);
t[x].dat=max(t[t[x].l].dat,t[t[x].r].dat);
return x;
}
}A;
struct SegmentTree2{
struct node{
int l,r,dat;
}t[N*4];
int cnt,root;
void clear(){cnt=root=0;}
int newnode(){
cnt++;
t[cnt].l=t[cnt].r=0;t[cnt].dat=1<<30;
return cnt;
}
void change(int &p,int l,int r,int x,int v){
if(!p) p=newnode();
if(l==r){
t[p].dat=v;
return;
}
int mid=(l+r)/2;
if(x<=mid) change(t[p].l,l,mid,x,v);
else change(t[p].r,mid+1,r,x,v);
t[p].dat=min(t[t[p].l].dat,t[t[p].r].dat);
}
int ask(int p,int l,int r,int ql,int qr,int x){
if(p==0||t[p].dat>=x) return 0;
if(l==r) return (t[p].dat<x)?l:0;
int mid=(l+r)/2;
if(ql<=l&&r<=qr){
if(t[t[p].r].dat<x) return ask(t[p].r,mid+1,r,ql,qr,x);
else return ask(t[p].l,l,mid,ql,qr,x);
}
else{
int ans=0;
if(qr>mid) ans=ask(t[p].r,mid+1,r,ql,qr,x);
if(ans==0&&ql<=mid) ans=ask(t[p].l,l,mid,ql,qr,x);
return ans;
}
}
}B;
void insert(int x,int y)
{
l++;
ver[l]=y;
nxt[l]=head[x];
head[x]=l;
}
void dfs1(int x,int pre)
{
size[x]=1;fa[x]=pre;
dep[x]=dep[pre]+1;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
dfs1(y,x);
size[x]+=size[y];
if(size[y]>size[son[x]]) son[x]=y;
}
}
void dfs2(int x,int t)
{
top[x]=t;
if(son[x]) dfs2(son[x],t);
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y!=son[x]) dfs2(y,y);
}
}
void dfs(int x)
{
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
dfs(y);
A.root[x]=A.merge(A.root[x],A.root[y]);
}
A.change(A.root[x],1,n,P.id[x],P.id[x]);
for(int i=0;i<v[x].size();i++){
int r=min(v[x][i].r,P.t[x].len+v[x][i].l);
if(v[x][i].l<=r-1) ans[v[x][i].id]=max(ans[v[x][i].id],A.ask(A.root[x],1,n,v[x][i].l,r-1));
}
}
void solve1(int x,int len)
{
if(P.id[x]) B.change(B.root,1,n,P.id[x],P.id[x]-len);
for(int i=head[x];i;i=nxt[i]) solve1(ver[i],len);
}
void solve(int x)
{
for(int i=x;i;i=son[i]){
for(int j=head[i];j;j=nxt[j]){
if(ver[j]!=son[i]) solve(ver[j]);
}
}
B.clear();
for(int i=x;i;i=son[i]){
for(int j=head[i];j;j=nxt[j]){
if(ver[j]!=son[i]) solve1(ver[j],P.t[i].len);
}
if(P.id[i]) B.change(B.root,1,n,P.id[i],P.id[i]-P.t[i].len);
for(int j=0;j<v[i].size();j++){
ans[v[i][j].id]=max(ans[v[i][j].id],B.ask(B.root,1,n,v[i][j].l,v[i][j].r-1,v[i][j].l));
}
}
}
int main()
{
P.cnt=P.last=1;B.t[0].dat=1<<30;
scanf("%s",s+1);
n=strlen(s+1);
for(i=1;i<=n;i++) P.extend(s[i],i);
for(i=2;i<=P.cnt;i++) insert(P.t[i].link,i);
scanf("%d",&m);
dfs1(1,0);dfs2(1,1);
for(i=1;i<=m;i++){
int l,r,x;
scanf("%d%d",&l,&r);
x=P.pos[r];a[i].l=l;a[i].r=r;
while(x){
v[x].push_back(query(l,r,i));
x=fa[top[x]];
}
}
dfs(1);
solve(1);
for(i=1;i<=m;i++){
if(ans[i]==0) puts("0");
else printf("%d\n",ans[i]-a[i].l+1);
}
return 0;
}