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

BZOJ 2124等差子序列 线段树&&hash

时间:2016-01-06 00:12:36      阅读:223      评论:0      收藏:0      [点我收藏+]

标签:

【题目描述 Description】

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

【输入描述 Input Description】

输入的第一行包含一个整数 T,表示组数。

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

【输出描述 Output Description】

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

【样例输入 Sample Input】

2

3

1 3 2

3

3 2 1

【样例输出 Sample Output】

N

Y

【数据范围及提示 Data Size & Hint】

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

【解题思路】

首先声明,此题开始并没有什么思路,只找到一个O(N^2)的算法,然而这并没有什么卵用。

老师明示暗示我要我用线段树去做,我苦思冥想没有想出来,于是就抄了题解。

题解是这样的,枚举等差中项,用一颗线段树去维护那些值选了,那些值没选,构成一个01串之后求一个哈希值。

如果出现中项左边的hash值和右边的hash值不一样的情况,就说明存在等差数列,因为证明有一个值在中项左边已经选过,并且与其对应的值在中项右边还没有选。

插入O(logn),查询O(logn),扫一遍O(n)整体O(ologn);

代码略丑

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=10000+10, mod=100000007;
int xp[maxn],a[maxn],n,v,t;
long long sumv[4*maxn][2];
//sumv[i][0] 代表从左边扫的值,sumv[i][1]代表从右边扫的值
void updata(int u,int l,int r){
    int lc=u<<1,rc=lc+1;
    if (l==r) sumv[u][0]=sumv[u][1]=1;
    else{
        int mid=(l+r)/2;
        if (v<=mid) updata(lc,l,mid);
        else updata(rc,mid+1,r);
        sumv[u][0]=(sumv[rc][0]+xp[r-mid]*sumv[lc][0]%mod)%mod;
        sumv[u][1]=(sumv[lc][1]+xp[mid-l+1]*sumv[rc][1]%mod)%mod;
    }
}

long long query(int node,int l,int r,int a,int b,int x){
    int lc=node<<1,rc=lc+1;
    if (l==a&&r==b) return sumv[node][x];
    int mid=(l+r)/2;
    long long left=0,right=0;
    if (mid<b) right=query(rc,mid+1,r,max(mid+1,a),b,x);
    if (a<=mid) left=query(lc,l,mid,a,min(mid,b),x);
    return (x?left+right*xp[max(0,mid-a+1)]%mod:right+left*xp[max(0,b-mid)]%mod)%mod;
}

int main(){
    scanf("%d",&t);
    for (int ii=0;ii<t;ii++){
        memset(sumv,0,sizeof(sumv));
        bool flag=0;
        scanf("%d",&n);
        xp[0]=1;
        for (int i=1;i<=n+5;i++) xp[i]=(xp[i-1]<<1)%mod;
        for (int i=0;i<n;i++)scanf("%d",&a[i]);
        for (int i=0;i<n;i++){
            int x=a[i];
            int len=min(x-1,n-x);//长度取短之后比较
            if (len) {
                int t1=query(1,1,n,x+1,x+len,1);
                int t2=query(1,1,n,x-len,x-1,0);
                if (t1!=t2){
                flag=1;
                break;
            }
            }
            v=x;
            updata(1,1,n);
        } 
        if (flag) printf("Y\n");
        else printf("N\n");
    }
}

 

BZOJ 2124等差子序列 线段树&&hash

标签:

原文地址:http://www.cnblogs.com/wuminyan/p/5103923.html

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