bzoj 2124: 等差子序列 (线段树+hash)

来源:互联网 发布:sql查询平均分大于60 编辑:程序博客网 时间:2024/06/06 14:20

题目描述

传送门

题目大意: 给出1…n的一个排列,问序列中是否存在一个长度>=3的等差数列

题解

这道题解题的关键就是给出的序列是1..n的一个排列。
根据等差数列的性质:等差数列 x,y,z 满足2*y=x+z,那么我们如果转换成位置关系的话,在数轴上x,z是关于y对称的。
那么对于每个y,什么时候才能形成等差数列呢?关于他对称的所有数对中存在一对数在序列中的顺序一个在他之前一个在他之后
那么我们可以建立一颗权值线段树,按照序列中的顺序依次插入a[i],如果[a[i]-len,a[i]-1]的hash值与[a[i]+len,a[i]+1]的hash值相同,那么一定不存在合法的数对。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 10003#define p 2000001001#define ull unsigned long long using namespace std;ull mi[N],tr[N*4],tr1[N*4];int a[N],T,n;ull update(ull x,ull y,int len){    return x*mi[len]+y;}void build(int now,int l,int r){    tr[now]=tr1[now]=0;    if (l==r) return;    int mid=(l+r)/2;    build(now<<1,l,mid);    build(now<<1|1,mid+1,r);}void pointchange(int now,int l,int r,int x){    if (l==r) {        tr[now]=1; tr1[now]=1;        return;    }    int mid=(l+r)/2;    if (x<=mid) pointchange(now<<1,l,mid,x);    else pointchange(now<<1|1,mid+1,r,x);    tr[now]=update(tr[now<<1],tr[now<<1|1],r-mid);    tr1[now]=update(tr1[now<<1|1],tr1[now<<1],mid-l+1);}ull find(int now,int l,int r,int ll,int rr){    if (ll>rr) return 0;    if (ll<=l&&r<=rr) return tr[now];    int mid=(l+r)/2;    ull ans=0; int len=0;    if (ll<=mid) ans=update(ans,find(now<<1,l,mid,ll,rr),len),len=min(r,rr)-mid;    if (rr>mid)  ans=update(ans,find(now<<1|1,mid+1,r,ll,rr),len);    return ans;}ull find1(int now,int l,int r,int ll,int rr){    if (ll>rr) return 0;    if (ll<=l&&r<=rr) return tr1[now];    int mid=(l+r)/2;    ull ans=0; int len=0;    if (ll<=mid) ans=update(find1(now<<1,l,mid,ll,rr),ans,len),len=mid-max(l,ll)+1;    if (rr>mid) ans=update(find1(now<<1|1,mid+1,r,ll,rr),ans,len);    return ans;}int main(){    freopen("a.in","r",stdin);    freopen("my.out","w",stdout);    scanf("%d",&T);    mi[0]=1;    for (int i=1;i<=10000;i++) mi[i]=mi[i-1]*p;    while (T--) {        scanf("%d",&n);        for (int i=1;i<=n;i++) scanf("%d",&a[i]);        build(1,1,n);        int mid=n/2;        if (n&1) mid++;        bool pd=false;        for (int i=1;i<=n;i++) {            ull x,y; int t;            if (a[i]<=mid) {              t=a[i]-1;              x=find(1,1,n,1,a[i]-1);              y=find1(1,1,n,a[i]+1,a[i]+t);              //cout<<x<<" "<<y<<endl;              if (x!=y) pd=true;            }            else {                t=n-a[i];                x=find(1,1,n,a[i]-t,a[i]-1);                y=find1(1,1,n,a[i]+1,n);                //cout<<x<<" "<<y<<endl;                if (x!=y) pd=true;            }            pointchange(1,1,n,a[i]);        }        if (pd) printf("Y\n");        else printf("N\n");    }}
0 0