计蒜客T3050 子串

计蒜客T3050 子串

题目链接:子串 - 题库 - 计蒜客 (jisuanke.com)


​ 不是常规操作,在线不好处理,考虑离线处理。对询问按照右端点排序。

​ 先考虑对于一个区间暴力怎么做,一种想法是枚举长度,然后对区间扫过去检查枚举的长度是否可行,这么暴力是 \(O(n^2)\) 的。注意到,我们的答案具有单调性,假设一个区间内最长特殊子串的长度为 \(x\),那么这个区间内也一定存在长度为 \(y(y<x)\) 的特殊子串。因为对于一个特殊子串,我们在子串中任意选两个点构成的子串也是特殊子串。那么我们考虑二分答案然后 \(O(n)\) 检查答案是否可行,这一来复杂度就变成了 \(O(n\log n)\)

​ 而这个序列其实还有一个性质,由于答案具有单调性,这使得对于每一个位置作为右端点,它最左端点的位置时单调不降的。 即假设对于一个端点,以它为右端点的最长特殊子串的左端点为 \(l\)。那么对于任意两个端点 \(i,j,(i<j)\)\(l_i\le l_j\)。我们可以采取反证法,如果 \(l_i>l_j\) 那么对于左端点在 \(l_j\) 右端点为 \(i\) 的子串,它也是特殊子串(满足题意的)。因为如第二段所言, \([l_j,j]\) 是特殊子串,那么 \([l_j,i]\) 也是特殊子串。那么既然左端点的位置单调不降,我们就可以采取双指针的策略在 \(O(n)\) 的复杂度内得出答案。

​ 但是我们有 \(q\) 个询问,总时间复杂度是 \(O(n\times q)\) 的。但是实际上每个点 \(i\) 来说,我们的 \(l_i\) 是固定的。所以我们只需要在外面做一遍双指针预处理出所有的 \(l_i\) 就行,然后查询时我们会发现一个问题,设当前查询的范围为 \(l,r\),对于任意 \(i\in[l,r]\)\(l_i\) 并不是一定都是大于 \(l\) 的。所以如果查询一个区间中 \(\max_{i=l}^{i=r}{i-l_i+1}\) 是不对的,我们考虑分开来处理,我们查询所有满足 \(l_i<l\) 的点的最大值 \(r_{max}\),然后查询所有满足 \(l_i\ge l\) 的点的最大的 \(i-l_i+1\)\(len\)。那么这个查询的答案就是 \(\max(r_{max}-l+1,len)\)

​ 上面这个查询操作我们可以使用权值线段树来实现,线段树维护两个值 \(r_{max},len\),意义与上一段中的相同,我们从 \(1\)\(n\) 遍历,对于当前遍历到的点 \(i\),我们在 \(l_i\) 这个位置插入 \(i,i-l_i+1\)。然后如果有查询的右端点也是 \(i\) 那么我们就如同上文一样查询答案。由于我们的查询按右端点排序了,所以不会有 \(l\le l_x\le r\)\(x>r\) 的点存在从而影响答案。

​ 那么总时间复杂度就是 \(O(n+n\log n)\)

代码如下:

#include<bits/stdc++.h>
#define ls(k) (k<<1)
#define rs(k) (k<<1|1)
using namespace std;
const int MAXN = 2e5+5; 
int n,m,cnt[MAXN],b[MAXN],pos[MAXN],ans[MAXN],a[MAXN];
struct Ques
{
	int l,r,id;
	bool operator < (const Ques &x)const
	{
		return r<x.r;
	}
}q[MAXN];
struct Tree
{
	int r,len;
}t[MAXN<<2];
void pushup(int k)
{
	t[k].r=max(t[ls(k)].r,t[rs(k)].r);
	t[k].len=max(t[ls(k)].len,t[rs(k)].len);
}
void upd(int pos,int k,int l,int r,int x)
{
	if(l==r)
	{
		t[k].r=x;
		t[k].len=x-pos+1;
		return ;
	}
	int mid=l+r>>1;
	if(pos<=mid) upd(pos,ls(k),l,mid,x);
	else upd(pos,rs(k),mid+1,r,x);
	pushup(k);
}
int query_r(int le,int ri,int k,int l,int r)
{
	if(le<=l&&r<=ri) return t[k].r;
	int mid=l+r>>1,ans=0;
	if(le<=mid) ans=max(ans,query_r(le,ri,ls(k),l,mid));
	if(ri>mid)  ans=max(ans,query_r(le,ri,rs(k),mid+1,r));
	return ans;
}
int query_len(int le,int ri,int k,int l,int r)
{
	if(le<=l&&r<=ri) return t[k].len;
	int mid=l+r>>1,ans=0;
	if(le<=mid) ans=max(ans,query_len(le,ri,ls(k),l,mid));
	if(ri>mid)  ans=max(ans,query_len(le,ri,rs(k),mid+1,r));
	return ans;
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i)
		scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+1+n);
	int Siz=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;++i)
		a[i]=lower_bound(b+1,b+1+Siz,a[i])-b;
	int r=1;
	for(int i=1;i<=n;++i)
	{
		if(i!=1) --cnt[a[i-1]];
		while(r<=n&&cnt[a[r]]==0)
		{
			pos[r]=i;
			++cnt[a[r]];
			++r;
		}
	}
	for(int i=1;i<=m;++i)
	{
		scanf("%d %d",&q[i].l,&q[i].r);
		if(q[i].l>q[i].r) swap(q[i].l,q[i].r);
		q[i].id=i;
	}
	sort(q+1,q+1+m);
	int now=1;
	for(int i=1;i<=n;++i)
	{
		upd(pos[i],1,1,n,i);
		while(now<=m&&q[now].r==i)
		{
			ans[q[now].id]=max(query_r(1,q[now].l,1,1,n)-q[now].l+1,query_len(q[now].l,q[now].r,1,1,n));
			++now;
		}
	}
	for(int i=1;i<=m;++i)
		printf("%d\n",ans[i]);
	return 0;
}
posted @ 2021-09-13 14:47  夜空之星  阅读(54)  评论(0编辑  收藏  举报