题目大意
给一个排列 h h ,求是否存在三元组,其中 i<j<k i < j < k ,使得 hi−hj=hj−hk h i − h j = h j − h k 。
解题思路
有这么几种想法。
第一种,将排列中奇偶性相同的放在一块,然后寻找中点出现了没。但这种方法显然有问题。因为新加入一个数后,有
O(n2)
O
(
n
2
)
种可能能够判断出答案,时间超限。
第二种,采用比较的方法,那么比较什么呢?肯定要比较序列的两边的东西。那么到底是用原序列,还是用反序列?反序列:
h′[h[i]]=i
h
′
[
h
[
i
]
]
=
i
。
如果用原序列,那么用每个数出现的位置来判定,每增加一个数,又会出现
O(n2)
O
(
n
2
)
种可能的三元组。所以方案不行。
如果考虑反序列,设当前位置上的数为
x
x
,则比较一下出现过的和没有出现过的数在线段树中的值。(0表示出现过,1表示没出现过)。对称地,以为中心,左边某段的哈希值和右边某段的哈希值相等,则说明存在了三元组。
特别地,要维护两种哈希值。包括从左到右和从右到左的。
心得
这种问题模型出现过很多,大概是给n个点,然后问中点的某些信息。
如果给的是排列,那么抓住“每个数只会出现一次”来作文章。
如果知道了每个数出现了一次,那么在扫描的时候是可以清楚地知道哪些数出现了,哪些数没出现。
设目前的01状态(该数字是否出现),如果改变了其中的一位,维护正确的答案的复杂度过高,那么很多时候是行不通的。
如果扫到一个数
x
x
<script type="math/tex" id="MathJax-Element-111">x</script>,则它前面的全是出现的,它后面是没有出现的。根据此性质可以运用到反数组中。
中点,也是一个很重要的研究点。
到底是以现在扫到的点为中点,还是通过现在扫到的点去寻找中点?研究方向的选择是很重要的。
像这道题目,如果以现在扫到的点为中点,那么在反数组中很容易根据对称性来判断出答案。
而比较两个序列的不同,可以用哈希判断。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 300010
#define mo 998244353
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
int tr[N*4],tr1[N*4];
int i,j,k,n,m,gx,gy;
int a[N],b[N];
int _2[N];
bool bz[N];
int read(){
int fh=1,res=0;char ch;
while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
if(ch=='-')fh=-1,ch=getchar();
while(ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+(ch^'0'),ch=getchar();
return fh*res;
}
void upl(int ps,int l,int r){
int wz=(l+r)>>1;
tr[ps]=((1ll*tr[ps<<1]*_2[r-wz])%mo+tr[ps<<1|1])%mo;
}
void upr(int ps,int l,int r){
int wz=(l+r)>>1;
tr1[ps]=((1ll*tr1[ps<<1|1]*_2[wz-l+1])%mo+tr1[ps<<1])%mo;
}
void bul(int ps,int l,int r,int x){
if(l==r){
tr[ps]=x;
return;
}
int wz=(l+r)>>1;
bul(ps<<1,l,wz,x);
bul(ps<<1|1,wz+1,r,x);
upl(ps,l,r);
}
void bur(int ps,int l,int r,int x){
if(l==r){
tr1[ps]=x;
return;
}
int wz=(l+r)>>1;
bur(ps<<1,l,wz,x);
bur(ps<<1|1,wz+1,r,x);
upr(ps,l,r);
}
void chl(int ps,int l,int r,int x,int z){
if(l==r){
tr[ps]=z;
return;
}
int wz=(l+r)>>1;
if(x<=wz)chl(ps<<1,l,wz,x,z);
else chl(ps<<1|1,wz+1,r,x,z);
upl(ps,l,r);
}
void chr(int ps,int l,int r,int x,int z){
if(l==r){
tr1[ps]=z;
return;
}
int wz=(l+r)>>1;
if(x<=wz)chr(ps<<1,l,wz,x,z);
else chr(ps<<1|1,wz+1,r,x,z);
upr(ps,l,r);
}
int gtl(int ps,int l,int r,int x,int y){
if(x>y)return 0;
if(l==x&&r==y)return tr[ps];
int wz=(l+r)>>1;
if(y<=wz)return gtl(ps<<1,l,wz,x,y);else
if(x>wz)return gtl(ps<<1|1,wz+1,r,x,y);else{
long long z1=gtl(ps<<1,l,wz,x,wz),z2=gtl(ps<<1|1,wz+1,r,wz+1,y);
return ((z1*_2[y-wz])%mo+z2)%mo;
}
}
int gtr(int ps,int l,int r,int x,int y){
if(x>y)return 0;
if(l==x&&r==y)return tr1[ps];
int wz=(l+r)>>1;
if(y<=wz)return gtr(ps<<1,l,wz,x,y);else
if(x>wz)return gtr(ps<<1|1,wz+1,r,x,y);else{
long long z1=gtr(ps<<1,l,wz,x,wz),z2=gtr(ps<<1|1,wz+1,r,wz+1,y);
return ((z2*_2[wz-x+1])%mo+z1)%mo;
}
}
int main(){
freopen("poem.in","r",stdin);
freopen("poem.out","w",stdout);
_2[0]=1;fo(i,1,300000)_2[i]=(_2[i-1]*2)%mo;
n=read();
fo(i,1,n)a[i]=read();
bul(1,1,n,1);
bur(1,1,n,1);
chl(1,1,n,a[1],0);
chr(1,1,n,a[1],0);
fo(i,2,n-1){
k=a[i]-1<n-a[i]?a[i]-1:n-a[i];
gx=gtl(1,1,n,a[i]-k,a[i]-1);
gy=gtr(1,1,n,a[i]+1,a[i]+k);
if(gx!=gy){
printf("YES");
return 0;
}
chl(1,1,n,a[i],0);
chr(1,1,n,a[i],0);
}
printf("NO");
return 0;
}