码迷,mamicode.com
首页 > 其他好文 > 详细

Bzoj2124 等差子序列

时间:2016-07-16 22:27:41      阅读:246      评论:0      收藏:0      [点我收藏+]

标签:

 

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”(两边长度要保持等于最短的一边的长度)。

 

 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 }

 

Bzoj2124 等差子序列

标签:

原文地址:http://www.cnblogs.com/SilverNebula/p/5676957.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!