zoukankan      html  css  js  c++  java
  • 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
    ");
            else printf("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
    ");
    71         else printf("N
    ");
    72     }
    73 }
  • 相关阅读:
    .net core 3.1 使用Redis缓存
    JavaSE 高级 第11节 缓冲输入输出字节流
    JavaSE 高级 第10节 字节数组输出流ByteArrayOutputStream
    JavaSE 高级 第09节 字节数组输入流ByteArrayInputStream
    JavaSE 高级 第08节 文件输出流FileOutputStream
    JavaSE 高级 第07节 文件输入流FileInputStream
    JavaSE 高级 第06节 初识I、O流
    JavaSE 高级 第05节 日期类与格式化
    JavaSE 高级 第04节 StringBuffer类
    JavaSE 高级 第03节 Math类与猜数字游戏
  • 原文地址:https://www.cnblogs.com/wuminyan/p/5103923.html
Copyright © 2011-2022 走看看