给定一个 1 1 1到 n n n的序列 a i a_i ai?,询问是否存在
1 ≤ p 1 < p 2 < p 3 < ? < p l e n ≤ n ( l e n ≥ 3 ) 1\leq p_1<p_2<p_3<\cdots<p_{len}\leq n \qquad (len\geq 3) 1≤p1?<p2?<p3?<?<plen?≤n(len≥3)
使得 a p 1 , a p 2 , a p 3 , … , a p l e n a_{p_1},a_{p_2},a_{p_3},\dots,a_{p_{len}} ap1??,ap2??,ap3??,…,aplen??是一个等差序列。
有 T T T组数据。
对于前 5 5 5个数据测试点, 1 ≤ n ≤ 5 × 1 0 5 , T ≤ 5 1\leq n\leq 5\times 10^5,T\leq 5 1≤n≤5×105,T≤5,时限为 5 s 5s 5s
对于后 20 20 20个数据测试点, 1 ≤ n ≤ 10000 , T ≤ 7 1\leq n\leq 10000,T\leq 7 1≤n≤10000,T≤7,时限为 2 s 2s 2s
题意可以简化为判断这个序列是否存在一个长度为 3 3 3的等差序列。
我们考虑在什么情况下会存在一个长度为 3 3 3的等差序列。如果存在一个 a i a_i ai?,使得 a i ? k a_i-k ai??k和 a i + k a_i+k ai?+k在 a i a_i ai?的异侧,那么这三个数就构成等差序列。
那么我们可以按 i i i从小到大来枚举 a i a_i ai?。因为 a i a_i ai?是一个 1 1 1到 n n n的排列,所以我们可以用一个数组 z z z来维护 a i a_i ai?的值。在遍历 a i a_i ai?的时候,在数组 z z z上将 a i a_i ai?左侧的点标为 1 1 1,右侧的点标为 0 0 0。
那么,如果不存在 k k k使得 a i ? k a_i-k ai??k和 a i + k a_i+k ai?+k在 a i a_i ai?的异侧,那么 a i ? k a_i-k ai??k和 a i + k a_i+k ai?+k的 z z z值就是相同的。我们将 z z z以 a i a_i ai?为中心对折,如果对于所有重合部分,重合的两个数都相等,那么就说明不存在 k k k使得 a i ? k a_i-k ai??k和 a i + k a_i+k ai?+k在 a i a_i ai?的异侧;否则,就存在一个长度为 3 3 3的等差序列。
那我们怎么判断重合的两个数是否都相等呢?我们发现这其实就等价于重合部分左半边和右半边对称,也就是这一段是回文的。那我们只需要维护 z z z数组的正序和倒序的哈希值,判断对应段的在两部分的哈希值是否相等。如果相等就说明不存在以 a i a_i ai?为中心的长度为 3 3 3的等差序列,否则就说明存在以 a i a_i ai?为中心的长度为 3 3 3的等差序列。
z z z数组的正序和倒序的哈希值可以用线段树来维护。
时间复杂度为 O ( n log ? n ) O(n\log n) O(nlogn)。
#include<bits/stdc++.h>
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=500000;
const long long mod=1e9+7;
int T,n,fl,a[N+5];
long long mi[N+5];
struct tree{
long long ly[5*N+5];
void build(int k,int l,int r){
ly[k]=0;
if(l==r) return;
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void down(int k,int l,int r){
int mid=l+r>>1;
ly[lc]=(ly[lc]+ly[k])%mod;
ly[rc]=(ly[rc]+ly[k]*mi[mid+1-l])%mod;
ly[k]=0;
}
void ch(int k,int l,int r,int x,int y){
if(l>=x&&r<=y){
ly[k]=(ly[k]+mi[l-x])%mod;
return;
}
if(ly[k]) down(k,l,r);
int mid=l+r>>1;
if(x<=mid) ch(lc,l,mid,x,y);
if(y>mid) ch(rc,mid+1,r,x,y);
}
long long find(int k,int l,int r,int x){
if(!x) return 0;
if(l==r&&l==x) return ly[k];
if(ly[k]) down(k,l,r);
int mid=l+r>>1;
if(x<=mid) return find(lc,l,mid,x);
else return find(rc,mid+1,r,x);
}
long long gt(int l,int r){
return (find(1,1,n,r)-find(1,1,n,l-1)*mi[r-l+1]%mod+mod)%mod;
}
}T1,T2;
int main()
{
mi[0]=1;
for(int i=1;i<=N;i++) mi[i]=mi[i-1]*2%mod;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
T1.build(1,1,n);
T2.build(1,1,n);
fl=0;
for(int i=2;i<=n;i++){
T1.ch(1,1,n,a[i-1],n);
T2.ch(1,1,n,n-a[i-1]+1,n);
int tmp=min(a[i]-1,n-a[i]);
int l=a[i]-tmp,r=a[i]+tmp;
if(T1.gt(l,r)!=T2.gt(n-r+1,n-l+1)){
fl=1;break;
}
}
if(fl) printf("Y\n");
else printf("N\n");
}
return 0;
}