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

【题目描述 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");
    }
}

 以上为堆状线段树,由于我一直喜欢用结构体,所以就又打了一个,然后发现内存时间代码复杂度都比堆要差,大概是因为要建树和结构体太大的缘故。线段树的种类的确要视题目而定。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int maxn=10000+10,mod=100000007;
 6 struct tree{
 7     int l,r,lch,rch;
 8     long long sum;
 9 }tr[maxn*2][2];
10 //tr[now][0]代表从左往右, tr[now][1]代表从右往左
11 int cnt,n,t,xp[maxn],a[maxn];
12 
13 void build(int now,int l,int r){
14     cnt++;
15     tr[cnt][0].l=tr[cnt][1].l=l; tr[cnt][0].r=tr[cnt][1].r=r;
16     if (l==r) return;
17     tr[cnt][0].lch=tr[cnt][1].lch=cnt+1;
18     int mid=(l+r)>>1;
19     build(cnt+1,l,mid);
20     tr[now][0].rch=tr[now][1].rch=cnt+1;
21     build(cnt+1,mid+1,r);
22 }
23 
24 long long query(int now,int l,int r,int x){
25     long long t1=0,t2=0;
26     if (tr[now][x].l==l&&tr[now][x].r==r) return tr[now][x].sum;
27     int mid=(tr[now][x].l+tr[now][x].r)>>1;
28     if (l<=mid)  t1=query(tr[now][x].lch,l,min(r,mid),x)%mod;
29     if (r>mid)  t2=query(tr[now][x].rch,max(mid+1,l),r,x)%mod;
30     if (x==0) return ((t1*xp[max(0,r-mid)])%mod+t2)%mod;
31     if (x==1) return ((t2*xp[max(mid-l+1,0)])%mod+t1)%mod;
32 //返回值的时候*xp的时候错过,乘的是数目,虽然我不知道我刚开始为什么写的不对 
33 }
34 
35 void insert(int now,int x){
36     if (tr[now][0].l==x&&tr[now][0].r==x){
37         tr[now][0].sum=tr[now][1].sum=1;
38         return;
39     }
40     int mid=(tr[now][0].l+tr[now][0].r)>>1;
41     if (x<=mid) insert(tr[now][0].lch,x);
42     if (x>=mid+1) insert(tr[now][0].rch,x);
43     int l=tr[now][0].l,r=tr[now][0].r;
44     tr[now][0].sum=((tr[tr[now][0].lch][0].sum*xp[r-mid])%mod
45     +tr[tr[now][0].rch][0].sum)%mod;
46     tr[now][1].sum=((tr[tr[now][1].rch][1].sum*xp[mid-l+1])%mod
47     +tr[tr[now][1].lch][1].sum)%mod;
48 }
49 
50 int main(){
51     scanf("%d",&t);
52     while (t--){
53         memset(tr,0,sizeof(tr));
54         cnt=0;//开始忘记清零CE了
55         scanf("%d",&n);
56         xp[0]=1;
57         bool flag=0;
58         for (int i=1;i<=n+5;i++) xp[i]=(xp[i-1]<<1)%mod;//预处理出所有二的幂 
59         build(1,1,n);
60         for (int i=0;i<n;i++) scanf("%d",&a[i]);
61         for (int i=0;i<n;i++){
62             int x=a[i];
63             int len=min(a[i]-1,n-a[i]);
64             if (len&&query(1,x-len,x-1,0)!=query(1,x+1,x+len,1)){
65                 flag=1;
66                 break;
67             }
68             insert(1,x);
69         }
70         if (flag) printf("Y\n");
71         else printf("N\n");
72     }
73 }

 

posted @ 2016-01-05 22:03  Alisahhh  阅读(255)  评论(0编辑  收藏  举报