bzoj2124 等差子序列 (树状数组 维护hash值)

83 篇文章 0 订阅
53 篇文章 0 订阅

bzoj2124 等差子序列

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=2124

题意:
T组数据。
给一个1到N的排列{Ai},询问是否存在1<=p1< p2< p3< p4< p5< …< pLen<=N (Len>=3),
使得Ap1,Ap2,Ap3,…ApLen是一个等差序列。

数据范围
N<=10000,T<=7

题解:

思路太神了。
其实就是是否存在三个数,按下标顺次排列成等差数列。
很容易想到O(n^2)的做法,就是枚举中间那个数ai,枚举x,看ai-x,ai+x是不是在ai的两侧。

反过来想,就是如果对于任意x,ai-x,ai+x都在ai的同侧的话,就不能以ai为中间的数,

因为是排列,每个数有且只有一个。
如果 出现状态用0/1表示,那么上述ai-x,ai+x都在ai的同侧的情况,就变成了:
值域上,以ai为中心的回文串

于是这是个字符串题。
如何快速判一个01串是否是回文串,且支持单点删除和修改?

Hash判断是否为回文串。存正反的Hash值即可;
支持单点修改,用树状数组存储Hash值即可。
复杂度 :O(n*logn )

树状数组维护hash值:
一个点x,代表的是[…,x]的一段区间,存储的是这一个区间的哈希值,
其中x那一端是低位,就是说向左base的位数变高,这样才能在查询和修改时快速地加加减减。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1000000007;
const int base=13131;
const int N=10005;
int T,n,a[N],po[N];
struct node
{
    int c[N];
    void init(){memset(c,0,sizeof(c));}
    void add(int x)
    {
        for(int i=x;i<=n;i=i+(i&(-i))) 
        c[i]=(c[i]+po[i-x])%mod;
    }
    int query(int x)
    {
        int ret=0;
        for(int i=x;i;i=i-(i&(-i)))
        ret=(1LL*po[x-i]*c[i]%mod+ret)%mod;
        return ret;
    } 
    int sum(int L,int R)
    {
        int ls=query(L-1); int rs=query(R);
        int ret=(rs-1LL*ls*po[R-L+1]%mod+mod)%mod;
        return ret;
    }
}Bit[2];
int main()
{
    scanf("%d",&T);
    po[0]=1; for(int i=1;i<=10000;i++) po[i]=(1LL*po[i-1]*base)%mod;
    while(T--)
    {
        Bit[0].init(); Bit[1].init();
        scanf("%d",&n); bool flag=0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        {
            int len=min(a[i]-1,n-a[i]);
            if(len&&Bit[0].sum(a[i]-len,a[i]-1)!=Bit[1].sum(n-(a[i]+len)+1,n-(a[i]+1)+1)) {flag=1; break;}
            Bit[0].add(a[i]); Bit[1].add(n-a[i]+1);
        }
        if(flag) printf("Y\n"); else printf("N\n");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值