[校内模拟]字符串

Description

定义一个字符串S的权值f(S)为,其所有不同后缀两两的LCP的最大值
给出一个字符串S,每个位置有权值ai
m次询问,每次询问给出[l,r,x],求一个[l,r]的子区间[a,b],满足f(S[a,b])>=x,且max(ai)最小
n,m<=5e4

Solution

这题分成两部分
1:给出[L,R],求f(S[L,R])
考虑SAM,枚举parent树上的一个点,显然只有其right集中相邻两个有用
用set启发式合并维护right集的过程中,我们能够找出n log n个有用的三元组(l,r,len),表示S[l-len+1,l]=S[r-len+1,r]
考虑所有满足r<=R的三元组,分为两种情况:
当L<=l-len,贡献为len
当l-len+1<=L<=l,贡献为l-L
两种都可以看做区间取max单点求值,用可持久化线段树维护即可
2:我们发现区间越大,f越大
考虑询问[L,R]的答案,有三种情况:包含左端点,包含右端点,不包含左端点和右端点
前两种情况可以直接二分,考虑第三种情况,在最大值不变的情况下区间越大越好,所以只有O(n)个有用区间,预处理出每一个的f值
把询问和区间按f排序,可以用二维数据结构维护,但是有更好的方法
注意到我们二分出了最小的p,满足[L,p]是一个合法区间
考虑所有有用区间的右端点r,若r∈[L,p]则已经被考虑,若r∈[p+1,R],l>=L则有贡献,若r∈[p+1,R],l<L则不会比[L,p]优,所以直接维护右端点在[p+1,R]中的最小值即可
时空复杂度O(n log^2 n)

Code

#include <set>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

int read() {
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

typedef set<int> :: iterator it;

const int N=5e4+5,M=8e6+5,inf=0x7fffffff;

int n,m,t0,a[N];
char st[N];

struct Pair{int x,y,len;}pr[N<<4];
bool cmp(Pair a,Pair b) {return a.y<b.y;}

namespace SAM{
	const int N=1e5+5;

	int son[N][26],len[N],fa[N],pos[N],tot,lst;
	set<int> s[N];
	vector<int> to[N];

	int extend(int p,int x) {
		int np=++tot;len[np]=len[p]+1;
		for(;p&&!son[p][x];p=fa[p]) son[p][x]=np;
		if (!p) fa[np]=1;
		else {
			int q=son[p][x];
			if (len[q]==len[p]+1) fa[np]=q;
			else {
				int nq=++tot;
				fo(i,0,25) son[nq][i]=son[q][i];
				fa[nq]=fa[q];len[nq]=len[p]+1;
				fa[q]=fa[np]=nq;
				for(;p&&son[p][x]==q;p=fa[p]) son[p][x]=nq;
			}
		}
		return np;
	}

	void dfs(int x) {
		for(int z:to[x]) {
			dfs(z);
			if (x==1) continue;
			if (s[z].size()>s[x].size()) swap(s[z],s[x]);
			for(int j:s[z]) {
				it k=s[x].lower_bound(j);
				if (k!=s[x].end()) pr[++t0].x=j,pr[t0].y=*k,pr[t0].len=len[x];
				if (k!=s[x].begin()) {
					k--;
					pr[++t0].x=*k,pr[t0].y=j,pr[t0].len=len[x];
				}
			}
			for(int j:s[z]) s[x].insert(j);
		}
	}

	void solve(char *st,int n) {
		tot=lst=1;
		fo(i,1,n) pos[i]=lst=extend(lst,st[i]-'a');
		fo(i,1,n) s[pos[i]].insert(i);
		fo(i,2,tot) to[fa[i]].push_back(i);
		dfs(1);
	}
}

struct Segment_Tree{
	int rt[N],mx[M],ls[M],rs[M],bel[M],tot;

	void modify(int &v,int l,int r,int x,int y,int z,int rt) {
		if (x>y) return;
		if (bel[v]!=rt) {
			mx[++tot]=mx[v];bel[tot]=rt;
			ls[tot]=ls[v];rs[tot]=rs[v];
			v=tot;
		}
		if (x<=l&&r<=y) {mx[v]=max(mx[v],z);return;}
		int mid=l+r>>1;
		if (x<=mid) modify(ls[v],l,mid,x,y,z,rt);
		if (y>mid) modify(rs[v],mid+1,r,x,y,z,rt);
	}

	int query(int v,int l,int r,int x) {
		if (!v) return 0;
		if (l==r) return mx[v];
		int mid=l+r>>1,tmp=mx[v];
		if (x<=mid) tmp=max(tmp,query(ls[v],l,mid,x));
		else tmp=max(tmp,query(rs[v],mid+1,r,x));
		return tmp;
	}
}t1,t2;

int tr[N<<2];

void ins(int v,int l,int r,int x,int y) {
	tr[v]=min(tr[v],y);
	if (l==r) return;
	int mid=l+r>>1;
	if (x<=mid) ins(v<<1,l,mid,x,y);
	else ins(v<<1|1,mid+1,r,x,y);
}

int que(int v,int l,int r,int x,int y) {
	if (x<=l&&r<=y) return tr[v];
	int mid=l+r>>1,tmp=inf;
	if (x<=mid) tmp=min(tmp,que(v<<1,l,mid,x,y));
	if (y>mid) tmp=min(tmp,que(v<<1|1,mid+1,r,x,y));
	return tmp;
}

int F(int l,int r) {
	int ret=t1.query(t1.rt[r],1,n,l),pos=t2.query(t2.rt[r],1,n,l);
	return max(ret,pos-l+1);
}

int sta[N],top,L[N],R[N],an[N];

struct Que{int l,r,x,y;}ask[N],c[N];
bool cmp1(Que a,Que b) {return a.x>b.x;}

int g[N][16],lg[N];

int query(int l,int r) {
	int z=lg[r-l+1];
	return max(g[l][z],g[r-(1<<z)+1][z]);
}

int main() {
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	n=read();m=read();
	scanf("%s",st+1);
	SAM::solve(st,n);
	sort(pr+1,pr+t0+1,cmp);
	int j=0;
	fo(i,1,n) {
		t1.rt[i]=t1.rt[i-1];t2.rt[i]=t2.rt[i-1];
		while (j<t0&&pr[j+1].y==i) {
			j++;
			t1.modify(t1.rt[i],1,n,1,pr[j].x-pr[j].len,pr[j].len,i);
			t2.modify(t2.rt[i],1,n,pr[j].x-pr[j].len+1,pr[j].x,pr[j].x,i);
		}
	}
	fo(i,1,n) g[i][0]=a[i]=read();
	fo(i,2,n) lg[i]=lg[i>>1]+1;
	fo(j,1,lg[n]) fo(i,1,n-(1<<j)+1) g[i][j]=max(g[i][j-1],g[i+(1<<j-1)][j-1]);
	sta[top=0]=0;
	fo(i,1,n) {
		while (top&&a[sta[top]]<=a[i]) top--;
		L[i]=sta[top]+1;sta[++top]=i;
	}
	sta[top=0]=n+1;
	fd(i,n,1) {
		while (top&&a[sta[top]]<=a[i]) top--;
		R[i]=sta[top]-1;sta[++top]=i;
	}
	fo(i,1,n) c[i].l=L[i],c[i].r=R[i],c[i].x=F(L[i],R[i]),c[i].y=a[i];
	fo(i,1,m) ask[i].l=read(),ask[i].r=read(),ask[i].x=read(),ask[i].y=i;
	sort(ask+1,ask+m+1,cmp1);
	sort(c+1,c+n+1,cmp1);
	fo(i,1,n<<2) tr[i]=inf;j=0;
	fo(i,1,m) {
		while (j<n&&c[j+1].x>=ask[i].x) {j++;ins(1,1,n,c[j].l,c[j].y);}
		int id=ask[i].y,l=ask[i].l,r=ask[i].r,x=ask[i].x;
		if (F(l,r)<x) {an[id]=-1;continue;}
		an[id]=inf;
		int L=l,R=r,pos;
		while (L<=R) {
			int mid=L+R>>1;
			if (F(l,mid)>=x) pos=mid,R=mid-1;
			else L=mid+1;
		}
		an[id]=min(an[id],query(l,pos));
		L=l,R=r;
		while (L<=R) {
			int mid=L+R>>1;
			if (F(mid,r)>=x) pos=mid,L=mid+1;
			else R=mid-1;
		}
		an[id]=min(an[id],query(pos,r));
		an[id]=min(an[id],que(1,1,n,l,pos));
	}
	fo(i,1,m) printf("%d\n",an[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值