zoukankan      html  css  js  c++  java
  • 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

    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
    ");
    70         else printf("N
    ");
    71     }
    72     return 0;
    73 }
  • 相关阅读:
    ncover
    bash
    .net framework 工具
    keePass
    jersey
    i-jetty
    如何查看set环境变量的更改
    C语言丨如果你不是程序员,绝对看不懂这三个符号!(= 和==、!=)
    忘记 root 密码怎么办?教你4种使用MySQL方式修改密码!(超实用)
    一线城市容不下肉体,二三线城市安放不了灵魂,程序员何处为家?
  • 原文地址:https://www.cnblogs.com/SilverNebula/p/5676957.html
Copyright © 2011-2022 走看看