简要题意:
给定一个长度为n的排列pi,问是否存在一个长度大于等于3的等差子序列。
思路:
首先,很显然的一点,我们只需要考虑是否存在长度等于3的等差子序列即可。
而且要注意到本题非常特殊的一点是排列,这意味着[1..n]中的每个数都恰好出现一次且仅出现一次。
那么当我们枚举等差子序列中间的那一项时,不妨设为a[mid],
那么假设等差子序列的第一项为a[l](l<mid),那么如果2*a[mid]-a[l]>=1 且 2*a[mid]-a[l]<=n,
那么2*a[mid]-a[l]要么落在mid左侧,要么落在mid右侧。
如果落在mid右侧,那么长度等于3的等差子序列即存在。
由于时限的关系,O(n^2)的做法会超时,因此我们显然不能在枚举中间项的同时再枚举第一项;
因此此时优化的点在于不枚举第一项,而是采用一种替代的方法,不通过枚举来得到我们需要的效果。
我们需要用一种数据结构来存储在mid左侧出现过的数字。
并且支持快速查找每一个[1..n]的数字x,2*a[mid]-x是否也同时在这个数据结构中出现。
如果2*a[mid]-x不在这个数据结构出现,且1<=2*a[mid]-x<=n,说明2*a[mid]-x一定在mid的右侧出现,那么说明此时我们需要的等差子序列出现了。
这启发我们用权值线段树来完成这个工作,
我们可以用一个01权值线段树来记录所有出现在mid左侧的数字,即x这个数值出现,则线段树上x的位置的值为1,否则为0;
那么相当于我们需要判断的是我们的01权值线段树是否关于a[mid]值对称。(当然细节需要注意,超出[1..n]的部分需要丢掉)
快速判断两个区间是否相同,这就属于字符串hash的功能范围内了。
因此,我们在权值线段树上同时维护区间从左到右的Hash值,从右到左的Hash值,当查询的时候只需要快速判断一下a[mid]两侧的Hash值是否相同即可。
如果不同,说明存在我们需要的等差子序列。
当我们移动mid时,对权值线段树做的修改就是单点修改,比较方便维护两个Hash值的变化。
至此,本题已经完成。时间复杂度为O(NlogN)。
#include<bits/stdc++.h> #define lson(p) p<<1 #define rson(p) p<<1|1 #define rep(i,a,b) for (int i=a;i<b;i++) #define per(i,a,b) for (int i=a-1;i>=b;i--) #define all(v) (v).begin() (v).end() #define fi first #define se second #define SZ(x) ((int)(x).size()) #define pb push_back #define mp make_pair using namespace std; typedef long long ll; typedef double db; typedef pair<int,int> PII; typedef vector<int> VI; clock_t startTime; mt19937 mrand(random_device{}()); const int mod=1e9+7; const int maxn=1e4+10; int pw[maxn]; int a[maxn]; int T,n; db getCurrentTime() { return (db)(clock()-startTime)/CLOCKS_PER_SEC; } struct node { int Lhash,Rhash; int len; int v; }b[maxn<<2]; void pushup(int p) { b[p].Lhash=(1LL*b[lson(p)].Lhash*pw[b[rson(p)].len]%mod+b[rson(p)].Lhash)%mod; b[p].Rhash=(1LL*b[rson(p)].Rhash*pw[b[lson(p)].len]%mod+b[lson(p)].Rhash)%mod; b[p].len=b[lson(p)].len+b[rson(p)].len; } void build(int p,int l,int r) { if (l==r) { b[p].len=1; b[p].Lhash=b[p].Rhash=0; b[p].v=0; return ; } int mid=(l+r)>>1; build(lson(p),l,mid); build(rson(p),mid+1,r); pushup(p); } void modify(int p,int l,int r,int pos) { if (l==r) { b[p].v=1; b[p].Lhash=b[p].Rhash=1; return ; } int mid=(l+r)>>1; if (pos<=mid) modify(lson(p),l,mid,pos); else modify(rson(p),mid+1,r,pos); pushup(p); } node getHash(int p,int l,int r,int L,int R) { if (L>R) { node e; e.Lhash=e.Rhash=0; return e; } if (L<=l&&r<=R) return b[p]; int mid=(l+r)>>1; if (R<=mid) return getHash(lson(p),l,mid,L,R); else if (L>mid) return getHash(rson(p),mid+1,r,L,R); else { node e1=getHash(lson(p),l,mid,L,R); node e2=getHash(rson(p),mid+1,r,L,R); node e; e.Lhash=(1LL*e1.Lhash*pw[e2.len]%mod+e2.Lhash)%mod; e.Rhash=(1LL*e2.Rhash*pw[e1.len]%mod+e1.Rhash)%mod; e.len=e1.len+e2.len; return e; } } int main() { pw[0]=1; for (int i=1;i<maxn;i++) pw[i]=1LL*pw[i-1]*233%mod; scanf("%d",&T); while (T--) { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); bool bo=false; for (int i=1;i<=n;i++) { int x=min(a[i]-1,n-a[i]); node l=getHash(1,1,n,a[i]-x,a[i]-1); node r=getHash(1,1,n,a[i]+1,a[i]+x); if (l.Rhash!=r.Lhash) { bo=true; break; } modify(1,1,n,a[i]); } if (bo) printf("Y "); else printf("N "); } return 0; }