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 }
  • 相关阅读:
    Jmeter_完成文件批量下载
    Jmeter_文件的下载
    Jmeter_针对一个账户批量上传文件
    Jmeter上传文件操作
    Jmeter_获取结果写到excel
    Jmeter_接口串联自动化测试_登录后充值获取cookie
    JS 对象(Object)和字符串(String)互转
    全国图书馆参考咨询联盟关闭文献传递功能解决办法
    Geodesic Distance:两点间的最短距离之法截弧/等角航线/测地线
    空间数据库基础理论 GIS空间数据处理分析涉及的基本概念
  • 原文地址:https://www.cnblogs.com/wuminyan/p/5103923.html
Copyright © 2011-2022 走看看