您的位置:首页 > 其它

Bzoj2124 等差子序列

2016-07-16 17:49 411 查看

 

Time Limit: 3 Sec  Memory Limit: 259 MB
Submit: 911  Solved: 337

Description

给一个1到N的排列{Ai},询问是否存在1<=p1=3),使得Ap1,Ap2,Ap3,…ApLen是一个等差序列。

Input

输入的第一行包含一个整数T,表示组数。下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开。

Output

对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则输出一行“N”。

Sample Input

2
3
1 3 2
3
3 2 1

Sample Output

N
Y

HINT

对于100%的数据,N<=10000,T<=7

 

  表达能力不好,但还是试图解释一下。

  有一种诡异的算法:

  用二进制表示一个数是否已经选了,在某个位置的数若是a[i],若其向左数的hash不等于向右数的hash,说明一定有a[i]+x和a[i]-x分别在a[i]所在位置的两边,也就是有解。

  ↑上面这行目测看不懂,举个例子:

  3
  3 2 1 这是题目中的数据。我们用01串表示1、2、3这三个数选了没有,初始时是“0 0 0”

  从左开始扫描,先选定3,此时01串表示为"0 0 1",3的位置在串最右(原数集中3最大),左右不可能有数和它构成等差序列。

  然后选定2,此时01串表示为“0 1 1”,以2的位置为中心向左数,得到0;向右数,在对称位置得到1,这说明“比2小1的数”和“比2大1的数”一个选了(在左边)一个没选(在右边),那么它们就能构成等差序列了。

  存储hash以判断01串是否相等,用线段树维护区间hash以方便查找,就大功告成了。

  注意:由于是要判断一个位置左右“对称位置”是否不等,所以要存正序和逆序的hash。再举个例子:

      有01串:0 1 0 1 [1] 0 1 1 0 1,从中心位置向左数得到的串是“1 0 1 0”,向右数得到的是“0 1 1 0”(两边长度要保持等于最短的一边的长度)。

 

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#define LL long long
#define ls l,mid,rt<<1
#define rs mid+1,r,rt<<1|1
using namespace std;
const int mod=1000000007;
const int mxn=100000;
int n;
LL hashL[mxn],hashR[mxn],pw[mxn];
int a[mxn];
void pushup(int rt,int len){
LL tmp=len>>1;
hashL[rt]=(hashL[rt<<1]*pw[tmp]+hashL[rt<<1|1])%mod;//更新左数hash
hashR[rt]=(hashR[rt<<1|1]*pw[len-tmp]+hashR[rt<<1])%mod;//更新右数hash
return;
}
void add(int x,int l,int r,int rt){
if(l==r){
hashL[rt]=hashR[rt]=1;
return;
}
int mid=(l+r)>>1;
if(x<=mid)add(x,ls);
else add(x,rs);
pushup(rt,r-l+1);
}
LL queryL(int L,int R,int l,int r,int rt){//查询hash
if(L>R)return 0;
if(L==l && r==R)return hashL[rt];
int mid=(l+r)>>1;
if(L>mid)return queryL(L,R,rs);
else if(R<=mid)return queryL(L,R,ls);
else return (queryL(L,mid,ls)*pw[R-mid]+queryL(mid+1,R,rs))%mod;
}
LL queryR(int L,int R,int l,int r,int rt){
if(L>R)return 0;
if(L==l && r==R)return hashR[rt];
int mid=(l+r)>>1;
if(L>mid)return queryR(L,R,rs);
else if(R<=mid)return queryR(L,R,ls);
else return (queryR(L,mid,ls)+queryR(mid+1,R,rs)*pw[mid+1-L])%mod;
}
int main(){
pw[1]=3;//进制,大于等于2任选
for(int i=2;i<=mxn;i++)pw[i]=(pw[i-1]*3)%mod;
int T;
scanf("%d",&T);
while(T--){
memset(hashL,0,sizeof hashL);//init
memset(hashR,0,sizeof hashR);
scanf("%d",&n);
int i,j;
for(i=1;i<=n;i++)scanf("%d",&a[i]);
bool flag=0;
for(i=1;i<=n;i++){
LL len=min(a[i]-1,n-a[i]);
LL t1=queryL(a[i]-len,a[i]-1,1,n,1);
LL t2=queryR(a[i]+1,a[i]+len,1,n,1);
if(t1!=t2){//左右hash不等,则可以形成等差序列
flag=1;
break;
}
add(a[i],1,n,1);//将该数加入hash
}
if(flag)printf("Y\n");
else printf("N\n");
}
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: