BZOJ2124 等差子序列(树状数组+哈希)

  容易想到一种暴力的做法:枚举中间的位置,设该位置权值为x,如果其两边存在权值关于x对称即合法。

  问题是如何快速寻找这个东西是否存在。考虑仅将该位置左边出现的权值标1。那么若在值域上若关于x对称的两权值标号不同,说明他们的位置分别在两侧,也就说明存在等差子序列。那么只需要判断整体是否相同,哈希即可。

  哈希值需要动态维护,容易想到树状数组/线段树。从左到右依次处理并维护两个树状数组记录正反哈希值。

 

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 10010
#define ul unsigned long long
int T,n,a[N];
ul tree[N],tree2[N],p[N];
void add(int k){ul x=p[k-1];while (k<=n) tree[k]+=x,k+=k&-k;}
void add2(int k){ul x=p[n-k];while (k) tree2[k]+=x,k-=k&-k;}
ul query(int k){ul s=0;while (k) s+=tree[k],k-=k&-k;return s;}
ul query2(int k){ul s=0;while (k<=n) s+=tree2[k],k+=k&-k;return s;}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj2124.in","r",stdin);
    freopen("bzoj2124.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    T=read();
    while (T--)
    {
        n=read();
        for (int i=1;i<=n;i++) a[i]=read();
        p[0]=1;for (int i=1;i<=n;i++) p[i]=p[i-1]*107;
        memset(tree,0,sizeof(tree));
        memset(tree2,0,sizeof(tree2));
        bool flag=0;
        for (int i=1;i<=n;i++)
        {
            if (a[i]-1<n-a[i])
            {
                if (query(a[i]-1)*p[n-(a[i]<<1)+1]!=query2(a[i]+1)-query2(a[i]<<1)) {flag=1;break;} 
            }
            else
            {
                if (query2(a[i]+1)*p[a[i]-(n-a[i])-1]!=query(a[i]-1)-query(a[i]-(n-a[i])-1)) {flag=1;break;}
            }
            add(a[i]);add2(a[i]);
        }
        if (flag) printf("Y\n");else printf("N\n");
    }
    return 0;
}

 

posted @ 2018-08-28 21:03  Gloid  阅读(219)  评论(0编辑  收藏  举报