终焉之排列

终焉之排列

题意:

给定一个序列 \(a\),求有没有点对满足 \(x\leq y \leq z\) ,有 \(a_x+a_z=2a_y\)

分析:

暴力写法当然是记录每个值出现的位置,然后对于每一个 \(a_i\) 向两边进行搜索。

但是 暴力是一种虫豸的写法 ,考虑正解。

我们设 \(h[x]=1/0\) 表示 \(x\) 是否在 \([1,j]\) 中出现/没出现。

\(len=min(n-x,x-1)\) ,如果 \(h[x-len \rightarrow x-1]=h[x+len \rightarrow x+1]\) 不相同,则证明有这样的点对。

可能理解起来有点抽象,我们取一组样例:

1 5 2 4 3
  • 当进行到第 \(3\) 次循环时,\(h[1 \rightarrow n]\) 可以表示为:
1 1 0 0 1

此时 \(a[i]=2\) ,则 \(len=min(5-2,2-1)=1\) ,则判断 \(h[1]\)\(h[3]\) 是否相同。

不相同,此时的意义是: \(1\)\(2\) 之前出现过,\(3\)\(2\) 之前没出现过,因此 \(2\) 肯定在 \(1,3\) 之间。

  • 当进行到第 \(4\) 次循环,\(h[1 \rightarrow n]\) 可以表示为:
1 1 0 1 1

此时 \(a[i]=4\) ,则 \(len=1\) ,判断 \(h[5]\)\(h[3]\) 是否相同:

不相同,此时的意义是 \(5\)\(4\) 之前出现过,\(3\)\(5\) 之前没出现过,因此 \(4\) 肯定在 \(3,5\) 之间。

可以看出,我们每次都这么算一遍,时间复杂度太炸了,因此需要一种数据结构,能够存储这个 \(0/1\) 串。

考虑字符串哈希:每个长度代表一个值,用线段树维护 \([l,r]\) 区间内 哈希值 的正值/反值,用两个数组维护。

判断 区间 \([x-len,x-1]\) 的正哈希值和 \([x+1,x+len]\) 的反哈希值是否相同,也就是区间 \(h[x-len,x-1]\)\(h[x+len,x+1]\) 是否相同。

如果相同,就证明不在中间,否则在中间,输出 \(YES\) 即可。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5,P=71;
int sum[2][N<<2],a[N],p[N];
int n,m,T;

void modify(int op,int x,int l,int r,int pos){
    if(l==r){
        sum[op][x]=1; return;
    }
    int mid=l+r>>1;
    if(pos<=mid) modify(op,x<<1,l,mid,pos);
    else         modify(op,x<<1|1,mid+1,r,pos);
    if(op==0) sum[op][x]=sum[op][x<<1]+sum[op][x<<1|1]*p[mid-l+1];
    else      sum[op][x]=sum[op][x<<1|1]+sum[op][x<<1]*p[r-mid];
}

int query(int op,int x,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        if(op==0) return p[l-L]*sum[op][x];
        else return      p[R-r]*sum[op][x];
    }
    int mid=l+r>>1,res=0;
    if(L<=mid) res+=query(op,x<<1,l,mid,L,R);
    if(R>mid)  res+=query(op,x<<1|1,mid+1,r,L,R);
    return res;
}

int main(){
    cin>>T;
    while(T--){
        cin>>n;  int flag=0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        p[0]=1; for(int i=1;i<=n;i++) p[i]=p[i-1]*P;
        memset(sum,0,sizeof(sum));
        for(int i=1;i<n-1;i++){
            modify(0,1,1,n,a[i]); modify(1,1,1,n,a[i]);
            int len=min(n-a[i+1],a[i+1]-1);
            if(len==0) continue;
            int Front=query(0,1,1,n,a[i+1]-len,a[i+1]-1);
            int  Back=query(1,1,1,n,a[i+1]+1,a[i+1]+len);
            if(Front!=Back){
                puts("YES"); flag=1; break;
            } 
        }
        if(flag==0) puts("NO");
    }
    system("pause");
    return 0;
}
posted @ 2021-10-14 21:48  Evitagen  阅读(73)  评论(0编辑  收藏  举报