[洛谷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;
}
posted @ 2021-02-07 17:34  CJlzf  阅读(130)  评论(0编辑  收藏  举报