方法:线段树维护hash值
解析:
搞了一下午的题,丧心病狂- -
第一发思路,开个数组,之中的每个数状压31个数然后枚举公差,之后绝壁T,后来听说这么做5分?
然后zxr讲的那个鬼思路,
01串代表某个值是否出现过,之后hash就好
每一次的a[i]这个值,我们只需要看其向左部分与向右部分最长相等长度的01串的hash值是否相等,如果相等则代表a[i]-x这个值与a[i]+x这个值在a[i]的同侧,即不可能构成一个长度为3的等差数列。
反之则两个数在不同侧,即可以构成一个长度为3的等差数列。
然后怎么维护hash? 线段树啊
两个hash(正序逆序)怎么维护?建两个数常数很大啊
hash值怎么合并?千万别用快速幂啊,预处理!
不然T死!
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 10010
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define p 3
#define mod 1000000007
using namespace std;
typedef long long ll;
ll hash[N<<2],hash2[N<<2];
ll pow[N];
int a[N];
int n,t;
void pushup(int rt,int m)
{
ll tmp=m>>1;
hash[rt]=(hash[rt<<1]*pow[tmp]+hash[rt<<1|1])%mod;
hash2[rt]=(hash2[rt<<1|1]*pow[m-tmp]+hash2[rt<<1])%mod;
}
void update(int pt,int l,int r,int rt)
{
if(l==r)
{
hash[rt]=hash2[rt]=1;
return;
}
int mid=(l+r)>>1;
if(pt<=mid)update(pt,lson);
else update(pt,rson);
pushup(rt,r-l+1);
}
ll query(int L,int R,int l,int r,int rt)
{
if(L>R)return 0;
int ans=0;
if(L==l&&r==R)
{
return hash[rt];
}
int mid=(l+r)>>1;
if(R<=mid)return query(L,R,lson);
else if(L>mid)return query(L,R,rson);
else return (query(L,mid,lson)*pow[R-mid]+query(mid+1,R,rson))%mod;
}
ll query2(int L,int R,int l,int r,int rt)
{
if(L>R)return 0;
if(L==l&&r==R)
{
return hash2[rt];
}
int mid=(l+r)>>1;
if(R<=mid)return query2(L,R,lson);
else if(L>mid)return query2(L,R,rson);
else return (query2(L,mid,lson)+query2(mid+1,R,rson)*pow[mid-L+1])%mod;
}
int main()
{
scanf("%d",&t);
pow[1]=p;
for(int i=2;i<=10000;i++)pow[i]=(pow[i-1]*p)%mod;
while(t--)
{
memset(hash,0,sizeof(hash));
memset(hash2,0,sizeof(hash2));
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int flag=0;
for(int i=1;i<=n;i++)
{
ll len=min(a[i]-1,n-a[i]);
ll tmp1=query(a[i]-len,a[i]-1,1,n,1);
ll tmp2=query2(a[i]+1,a[i]+len,1,n,1);
if(tmp1!=tmp2)
{
flag=1;break;
}
update(a[i],1,n,1);
}
if(flag)printf("Y\n");
else printf("N\n");
}
}