Bzoj2124 等差子序列
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
3
1 3 2
3
3 2 1
Sample Output
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”(两边长度要保持等于最短的一边的长度)。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define LL long long 7 #define ls l,mid,rt<<1 8 #define rs mid+1,r,rt<<1|1 9 using namespace std; 10 const int mod=1000000007; 11 const int mxn=100000; 12 int n; 13 LL hashL[mxn],hashR[mxn],pw[mxn]; 14 int a[mxn]; 15 void pushup(int rt,int len){ 16 LL tmp=len>>1; 17 hashL[rt]=(hashL[rt<<1]*pw[tmp]+hashL[rt<<1|1])%mod;//更新左数hash 18 hashR[rt]=(hashR[rt<<1|1]*pw[len-tmp]+hashR[rt<<1])%mod;//更新右数hash 19 return; 20 } 21 void add(int x,int l,int r,int rt){ 22 if(l==r){ 23 hashL[rt]=hashR[rt]=1; 24 return; 25 } 26 int mid=(l+r)>>1; 27 if(x<=mid)add(x,ls); 28 else add(x,rs); 29 pushup(rt,r-l+1); 30 } 31 LL queryL(int L,int R,int l,int r,int rt){//查询hash 32 if(L>R)return 0; 33 if(L==l && r==R)return hashL[rt]; 34 int mid=(l+r)>>1; 35 if(L>mid)return queryL(L,R,rs); 36 else if(R<=mid)return queryL(L,R,ls); 37 else return (queryL(L,mid,ls)*pw[R-mid]+queryL(mid+1,R,rs))%mod; 38 } 39 LL queryR(int L,int R,int l,int r,int rt){ 40 if(L>R)return 0; 41 if(L==l && r==R)return hashR[rt]; 42 int mid=(l+r)>>1; 43 if(L>mid)return queryR(L,R,rs); 44 else if(R<=mid)return queryR(L,R,ls); 45 else return (queryR(L,mid,ls)+queryR(mid+1,R,rs)*pw[mid+1-L])%mod; 46 } 47 int main(){ 48 pw[1]=3;//进制,大于等于2任选 49 for(int i=2;i<=mxn;i++)pw[i]=(pw[i-1]*3)%mod; 50 int T; 51 scanf("%d",&T); 52 while(T--){ 53 memset(hashL,0,sizeof hashL);//init 54 memset(hashR,0,sizeof hashR); 55 scanf("%d",&n); 56 int i,j; 57 for(i=1;i<=n;i++)scanf("%d",&a[i]); 58 bool flag=0; 59 for(i=1;i<=n;i++){ 60 LL len=min(a[i]-1,n-a[i]); 61 LL t1=queryL(a[i]-len,a[i]-1,1,n,1); 62 LL t2=queryR(a[i]+1,a[i]+len,1,n,1); 63 if(t1!=t2){//左右hash不等,则可以形成等差序列 64 flag=1; 65 break; 66 } 67 add(a[i],1,n,1);//将该数加入hash 68 } 69 if(flag)printf("Y\n"); 70 else printf("N\n"); 71 } 72 return 0; 73 }