2020年11月26日提高组 D 勇者的后缀

R e s u l t Result Result

...


H y p e r l i n k Hyperlink Hyperlink

https://www.luogu.com.cn/problem/U142356?contestId=37784


D e s c r i p t i o n Description Description

给定一个长度为 n n n的串, m m m个询问
每次询问一个 i , l , r i,l,r i,l,r表示 [ l , r ] [l,r] [l,r]开头的后缀与 i i i开头的后缀的 L C P LCP LCP,如果有多组解,输出字典序最小的那个


S o l u t i o n Solution Solution

首先建立出后缀数组然后 R M Q RMQ RMQ L C P LCP LCP

接着考虑一个区间的答案必然是这个区间内离 r k i rk_i rki最近的那两个中产生,也就是前驱和后继
区间前驱后继可以用主席树维护

如果后继更大,直接输出后继即可
如果前驱更大,由于我们要字典序最小,所以我们再二分一个值,直到后继大于前驱时,再输出后继

上述数据结构、二分之间都是分开的,所以时间复杂度为 O ( ( n + m ) log ⁡ n ) O((n+m)\log n) O((n+m)logn)


C o d e Code Code

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define N 200010
using namespace std;int n,m,q,res,id;
char s[N];
int x[N],y[N],c[N],sa[N],rk[N];
inline void Qsort()
{
	for(register int i=1;i<=m;i++) c[i]=0;
	for(register int i=1;i<=n;i++) ++c[x[i]];
	for(register int i=2;i<=m;i++) c[i]+=c[i-1];
	for(register int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
	return;
}
inline void get_SA()
{
	for(register int i=1;i<=n;i++) ++c[x[i]=s[i]];
	for(register int i=2;i<=m;i++) c[i]+=c[i-1];
	for(register int i=n;i>=1;i--) sa[c[x[i]]--]=i;
	for(register int k=1;k<=n;k<<=1)
	{
		int num=0;
		for(register int i=n-k+1;i<=n;i++) y[++num]=i;
		for(register int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
		Qsort();swap(x,y);
		x[sa[1]]=1;num=1;
		for(register int i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
		if(num==n) break;m=num;
	}
	return;
}
int h[N],height[N];
inline void get_height()
{
	int k=0;
	for(register int i=1;i<=n;i++) rk[sa[i]]=i;
	for(register int i=1;i<=n;i++)
	{
		if(k) --k;
		int j=sa[rk[i]-1];
		while(s[i+k]==s[j+k]) k++;
		height[rk[i]]=k;
	}
	return;
}
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
int f[N][21],log[N]={0,0,1};
inline int LCP(int l,int r)
{
	if(l+r==0) return -1;
	if(l==r) return n-sa[l]+1;
	if(l>r) swap(l,r);
	l++;int z=log[r-l+1];
	return min(f[l][z],f[r-(1<<z)+1][z]);
}
int cnt,rt[N],L[N<<5],R[N<<5],sum[N<<5];
inline int build(int l,int r)
{
	int rt=++cnt,mid=l+r>>1;
	if(l==r) {L[rt]=R[rt]=rt;return rt;}
	L[rt]=build(l,mid);R[rt]=build(mid+1,r);
	return rt;
}
inline int updata(int pre,int l,int r,int x)
{
	int rt=++cnt,mid=l+r>>1;
	L[rt]=L[pre];R[rt]=R[pre];sum[rt]=sum[pre]+1;
	if(l<r)
	{
		if(x<=mid) L[rt]=updata(L[pre],l,mid,x);
		else R[rt]=updata(R[pre],mid+1,r,x);
	}
	return rt;
}
inline int query(int p,int q,int l,int r,int k)
{
	if(l>=r) return l;
	int mid=l+r>>1,s=sum[L[q]]-sum[L[p]];
	if(s>=k) return query(L[p],L[q],l,mid,k);
	else return query(R[p],R[q],mid+1,r,k-s);
}
inline int Ask(int p,int q,int l,int r,int ql,int qr)
{
	if(ql>qr||(sum[q]-sum[p])==0) return 0;
	if(ql<=l&&r<=qr) return sum[q]-sum[p];
	int mid=l+r>>1,res=0;
	if(ql<=mid) res+=Ask(L[p],L[q],l,mid,ql,qr);
	if(qr>mid) res+=Ask(R[p],R[q],mid+1,r,ql,qr);
	return res;
}
signed main()
{
	scanf("%s",s+1);n=strlen(s+1);m=127;
	get_SA();get_height();
	for(register int i=1;i<=n;i++) f[i][0]=height[i];
	for(register int i=3;i<=n;i++) log[i]=log[i>>1]+1;
	for(register int j=1;1<<j<=n;j++) for(register int i=1;i+(1<<j)-1<=n;i++) 
	 f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
	q=read();
	rt[0]=build(1,n);
	for(register int i=1;i<=n;i++) rt[i]=updata(rt[i-1],1,n,rk[i]);
	while(q--)
	{
		int x=read(),l=read(),r=read(),lower=0,upper=0;
		x=rk[x];
		int Ans=0,Sum=Ask(rt[l-1],rt[r],1,n,1,x-1);
		if(Sum<sum[rt[r]]-sum[rt[l-1]]) upper=query(rt[l-1],rt[r],1,n,Sum+1);
		if(Sum>0) lower=query(rt[l-1],rt[r],1,n,Sum);
		Ans=LCP(x,upper);Sum=LCP(lower,x);
		if(Ans>Sum) 
		{
			printf("%d %d\n",Ans,sa[upper]);
			continue;
		}
		int L=1,R=x;
		while(L<=R)
		{
			int mid=(L+R)>>1;
			if(LCP(mid,x)<Sum) L=mid+1;else R=mid-1;
		}
		upper=Ask(rt[l-1],rt[r],1,n,1,L-1);
		printf("%d %d\n",Sum,sa[query(rt[l-1],rt[r],1,n,upper+1)]);
	}
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页