计蒜客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;
}