BZOJ 2124 等差子序列 (树状数组 hash)

2124: 等差子序列

Time Limit: 3 Sec Memory Limit: 259 MB
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

思路:
考虑枚举中间数x,如果x满足条件,也就是比它大和小的两个数y和z,x-y=z-x,并且这两个数要在x的两侧(如果我们考虑逐个枚举x,条件就是y已经出现过了而z还没有出现)。
因此,我们可以得到一个01串,其中x如果出现过,那么为1否则为0。
发现题目给的是1到n的排列,就是说每个数出现有且仅有一次,一个数之前只出现了等差数列中比它自身小的数,那么之后就一定会出现剩下那个比它大的数。
因此如果x满足条件,那么必然存在一个公差y使得[x-y]!=[x+y](01值不同,表示不在x的同一侧)。所以枚举等差中项,每次O(n)扫一遍左右两部分,然后我们发现问题就是判断以x为中心的极长字符串是否是回文串,如果不是那么显然存在y使得[x-y]!=[x+y]。
于是考虑hash,然后因为需要修改那么就用两个树状数组分别维护它所在区间正序和倒序的hash值即可。
hash真的很迷呀(来自一个没怎么写过的辣基)觉得自己的hash太丑陋了,只有慢慢做题摸索了。。。

#include <cstdio>  
#include <cstring>
#include <iostream>  
#define LL long long  
using namespace std;

int const mod = 1000000007;
int const N = 10010;
int n, a[N], pw[N]; 

struct bit{  
    int c[N];//树状数组维护区间hash 
    void clear(){ memset(c, 0, sizeof(c)); }  
    void add(int x){
        for(int i=x; i<=n; i+=(i&-i)) c[i] = (c[i] + pw[i-x]) % mod;//乘上距离(公差)的hash 
    }  
    int query(int x){  
        int sum = 0; 
        for(int i=x; i; i-=(i&-i)) 
            sum = ( (LL) c[i] * pw[x-i] + sum ) % mod;//乘上距离(公差)的hash 
        return sum;  
    }  
    int Query(int l, int r){  
        int p = query(l-1), q = query(r);
        return ( q - (LL) p * pw[r-l+1] % mod + mod) % mod;//乘上区间大小的hash
    }  
}order, reorder;//正序,反序 

int main(){
    int T; scanf("%d", &T);  
    pw[0] = 1;
    for(int i=1; i<=10000; i++) 
        pw[i] = (LL)pw[i-1] * 107 % mod;
    while( T-- ){
        order.clear(); reorder.clear(); 
        scanf("%d", &n);
        for(int i=1; i<=n; i++) 
            scanf("%d", &a[i]);  
        int i; 
        for( i=1; i<=n; i++){  
            int cc = min(a[i]-1,n-a[i]);//左右个数要相等,所以取其中小的一边 
            if(cc && order.Query(a[i]-cc, a[i]-1) != reorder.Query(n-a[i]-cc+1, n-a[i])) break;
            //可以找到等差  
            order.add(a[i]); reorder.add(n-a[i]+1);  
        }  
        puts( (i > n) ? "N" : "Y" );  
    }  
    return 0;  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值