BZOJ 2124 等差子序列 线段树维护hash值

20 篇文章 0 订阅
10 篇文章 0 订阅

题意:链接

方法:线段树维护hash值

解析:

搞了一下午的题,丧心病狂- -

第一发思路,开个数组,之中的每个数状压31个数然后枚举公差,之后绝壁T,后来听说这么做5分?

然后zxr讲的那个鬼思路,

01串代表某个值是否出现过,之后hash就好

每一次的a[i]这个值,我们只需要看其向左部分与向右部分最长相等长度的01串的hash值是否相等,如果相等则代表a[i]-x这个值与a[i]+x这个值在a[i]的同侧,即不可能构成一个长度为3的等差数列。

反之则两个数在不同侧,即可以构成一个长度为3的等差数列。

然后怎么维护hash? 线段树啊

两个hash(正序逆序)怎么维护?建两个数常数很大啊

hash值怎么合并?千万别用快速幂啊,预处理!

不然T死!

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 10010
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define p 3
#define mod 1000000007 
using namespace std;
typedef long long ll;
ll hash[N<<2],hash2[N<<2];
ll pow[N];
int a[N];
int n,t;
void pushup(int rt,int m)
{
    ll tmp=m>>1;
    hash[rt]=(hash[rt<<1]*pow[tmp]+hash[rt<<1|1])%mod;
    hash2[rt]=(hash2[rt<<1|1]*pow[m-tmp]+hash2[rt<<1])%mod;
}
void update(int pt,int l,int r,int rt)
{
    if(l==r)
    {
        hash[rt]=hash2[rt]=1;
        return;
    }
    int mid=(l+r)>>1;
    if(pt<=mid)update(pt,lson);
    else update(pt,rson);
    pushup(rt,r-l+1);
}
ll query(int L,int R,int l,int r,int rt)
{
    if(L>R)return 0;
    int ans=0;
    if(L==l&&r==R)
    {
        return hash[rt];
    }
    int mid=(l+r)>>1;
    if(R<=mid)return query(L,R,lson);
    else if(L>mid)return query(L,R,rson);
    else return (query(L,mid,lson)*pow[R-mid]+query(mid+1,R,rson))%mod;
}
ll query2(int L,int R,int l,int r,int rt)
{
    if(L>R)return 0;
    if(L==l&&r==R)
    {
        return hash2[rt];
    }
    int mid=(l+r)>>1;
    if(R<=mid)return query2(L,R,lson);
    else if(L>mid)return query2(L,R,rson);
    else return (query2(L,mid,lson)+query2(mid+1,R,rson)*pow[mid-L+1])%mod;
}
int main()
{
    scanf("%d",&t);
    pow[1]=p;
    for(int i=2;i<=10000;i++)pow[i]=(pow[i-1]*p)%mod;
    while(t--)
    {
        memset(hash,0,sizeof(hash));
        memset(hash2,0,sizeof(hash2));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        int flag=0;
        for(int i=1;i<=n;i++)
        {
            ll len=min(a[i]-1,n-a[i]);
            ll tmp1=query(a[i]-len,a[i]-1,1,n,1);
            ll tmp2=query2(a[i]+1,a[i]+len,1,n,1);
            if(tmp1!=tmp2)
            {
                flag=1;break;
            }
            update(a[i],1,n,1);
        }
        if(flag)printf("Y\n");
        else printf("N\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值