题解[P7361拜神]

原题链接

题意:给定一个字符串,每次询问一段区间中出现两次的最长子串长度。

思路还较清新,先将询问按右端点 \(r\) 排序,看对每个询问 \(l\) 能怎么方便求出。

将每个 \([1,r]\) 内的子串 \(p\) ,其在 \([1,r]\) 中次后出现的位置的右端点与左端点看做一条线段 \([s_p,t_p]\)。

那对每个 \(l\) 只需查出 \(\mathrm{Max}\left\{\left(\mathop{\mathrm{Max}}\limits_{s_p\leq l\leq t_p}t_p-l+1\right),\left(\mathop{\mathrm{Max}}\limits_{s_p\geq l}\ t_p-s_p+1\right)\right\}\)

这相当于是找出覆盖 \(l\) 的线段中的右端点最大值 \(-l\) ,与及不覆盖 \(l\) 且在 \(l\) 右侧的线段长度最大值的最大值。

由于对于 \(\text{endpos}\) 相同的子串右端点集合相同,且上式需要的线段是越长越好。

于是我们需要的线段仅为 \(\text{endpos}\) 相同的串中最长的一个,先想如何更新次右端点。

联系一下这题,不难得出 \(\text{LCT}\) 维护的方法。

就是建出 \(\text{parent tree}\) 后记录每个等价类出现位置的最右位置,以及次右位置。

\(r\) 向右移时单次 \(access\) 更改为了求上式需维护的信息,同时打上将次右值变成最右值,最右值更新为 \(r\) 的标记。

剩下的就是考虑上式怎么求出。

首先第二个 \(\mathrm{Max}\) 比较好做,因为每个子串的 \(s_p\) 单调递增,可以直接将 \([1,s_p]\) 的信息与 \(t_p-s_p+1\) 取最大值。

对于第一个 \(\mathrm{Max}\) 先考虑没有 \(access\) 顺带需要的修改该怎么做。

这可以将每个在 \([s_p,t_p]\) 之间的点,用 \(t_p\) 更新能覆盖这个点的线段最大右端点。

观察到每次更改所影响到的线段可视为向右平移一段区间,事实上之前的区间取最大值并不会影响之后的查询。

这是因为之后查询的 \(l\) 如果在原先 \(t_p\) 之后,就不会影响答案。

而在原先 \(t_p\) 之前的 \(l\) 必定会查到右移之后的的整个字符串,或者之后的右端点比之前右端点靠右。这两种情况分别对应右移前后,是否与原先字符串重合。而这两种情况一定会在第一、二个 \(\mathrm{Max}\) 中得出的长度比之前优,会将之前的标记覆盖掉。

于是只需要一个支持区间取 \(\mathrm{Max}\) 的数据结构,用线段树容易实现。

最终时间复杂度是 \(O(n\log^2n)\) 的。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,x,xx,y,tot=1,last=1,tmp,l,r;char ch;
int rot_xx,rot_y,s_i,acs_y,a;bool rot_b;
int mxlen[N],link[N],trans[N][26],f[N],res[N];
int lst[N],lst_[N],son[N][2],anc[N],cov[N];
inline void read(int &x){
	x=0;ch=getchar();while(ch<48||ch>57)ch=getchar();
	while(ch>47&&ch<58)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
void write(int x){if(x>9)write(x/10);putchar(48+x%10);}
#define ls k<<1
#define rs k<<1|1
struct segment_tree{
	int tag[N<<2],res;//这里用了标记永久化实现区间取max,单点查
	void update(int k,int l,int r,int x,int y,int v){
		if(x<=l&&r<=y)tag[k]=max(tag[k],v);
		else {
			int mid=(l+r)>>1;
			if(x<=mid)update(ls,l,mid,x,y,v);
			if(mid<y)update(rs,mid+1,r,x,y,v);
		}
	}
	void inquiry(int k,int l,int r,int pos){
		res=max(res,tag[k]);
		if(l^r){
			int mid=(l+r)>>1;
			if(pos<=mid)inquiry(ls,l,mid,pos);
			else inquiry(rs,mid+1,r,pos);
		}
	}
}S,S_;
inline void extend(int a){
	tmp=++tot;y=last;mxlen[tmp]=mxlen[y]+1;
	for(;y&&!trans[y][a];y=link[y])trans[y][a]=tmp;
	if(!y)link[tmp]=1;
	else {
		x=trans[y][a];
		if(mxlen[x]==mxlen[y]+1)link[tmp]=x;
		else {
			xx=++tot;mxlen[xx]=mxlen[y]+1;link[xx]=link[x];
			memcpy(trans[xx],trans[x],sizeof(trans[xx]));
			for(;y&&trans[y][a]==x;y=link[y])trans[y][a]=xx;
			link[tmp]=link[x]=xx;
		}
	}
	last=tmp;
}
inline bool nroot(int x){return son[anc[x]][0]==x||son[anc[x]][1]==x;}
inline bool p(int x){return son[anc[x]][1]==x;}
inline void cov_(int x,int v){lst_[x]=lst[x];lst[x]=cov[x]=v;}
//将次右变为最右,最右由覆盖标记更新
inline void pushdown(int x){
	if(cov[x]){
		if(son[x][0])cov_(son[x][0],cov[x]);
		if(son[x][1])cov_(son[x][1],cov[x]);
		cov[x]=0;
	}
}
void pushall(int x){if(nroot(x))pushall(anc[x]);pushdown(x);}
inline void rotate(int x){
	rot_y=anc[x];rot_xx=anc[rot_y];rot_b=p(x);
	if(nroot(rot_y))son[rot_xx][p(rot_y)]=x;
	anc[x]=rot_xx;
	anc[son[rot_y][rot_b]=son[x][!rot_b]]=rot_y;
	anc[son[x][!rot_b]=rot_y]=x;
}
inline void splay(int x){
	pushall(x);
	for(;s_i=anc[x],nroot(x);rotate(x))if(nroot(s_i))rotate(p(x)==p(s_i)?s_i:x);
}
inline void access(int x,int pos){
	for(y=0;x;y=x,x=anc[x]){
		splay(x);son[x][1]=y;
		l=lst[x]-mxlen[x]+1;r=lst[x];//l,r 为这个整个子串将要移动到的区间
		if(lst[x]&&x^1)S.update(1,1,n,l,r,r),S_.update(1,1,n,1,l,r-l+1);
	}//这里access由于每次是之前跳父亲来的,能保证x一定是当前splay中mxlen最大的
	cov_(y,pos);
}
int to[N],nextn[N],h[N],id[N],edg;
inline void add(int x,int y,int i){to[++edg]=y,nextn[edg]=h[x],h[x]=edg;id[edg]=i;}
main(){
	read(n);read(m);while(ch<97)ch=getchar();
	register int i,j;
	for(i=1;i<=n;++i)a=ch-97,extend(a),f[i]=tmp,ch=getchar();
	for(i=1;i<=tot;++i)anc[i]=link[i];
	for(i=1;i<=m;++i)read(x),read(y),add(y,x,i);
	for(i=1;i<=n;++i){
		access(f[i],i);
		for(j=h[i];j;j=nextn[j]){
			S.res=S_.res=0;
			S.inquiry(1,1,n,to[j]);
			S_.inquiry(1,1,n,to[j]);
			res[id[j]]=max(S.res-to[j]+1,S_.res);
		}
	}
	for(i=1;i<=m;++i)write(res[i]),putchar('\n');
}
上一篇:【分块】P4135 作诗


下一篇:[复习笔记]WQS二分