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

17 篇文章 0 订阅

题目描述

传送门

题目大意: 给出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");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值