【bzoj2124】等差子序列 权值线段树维护hash

Description

给一个1到N的排列{Ai},询问是否存在1<=p1=3),使得Ap1,Ap2,Ap3,…ApLen是一个等差序列。

Input

输入的第一行包含一个整数T,表示组数。下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开。

Output

对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则输出一行“N”。

Sample Input

2

3

1 3 2

3

3 2 1

Sample Output

N

Y 

HINT

对于100%的数据,N<=10000,T<=7

Source


神脑洞

只需要看是否能构成长度为三的等差数列即可。枚举中项x,对于一个数x,从左到右添加到集合中,若x-d和x+d都已经出现了,则x必定不合法。

那么出现过记为1,没出现记为0,则得到了一个{1…x-1}和{x+1…n}的01串。若有一个不相同则表明存在一个x-d和x+d只出现了一次,则另一个肯定在后面出现,这便构成一个等差数列。

所以每次把x丢进权值线段树里,查询前面和后面的hash值就行了。记住朝左和朝右的长度要相同……因为这个WA了一次

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;
const int SZ = 10010;
const int mod = 1000000007;

struct segment{
    int l,r;
    int h[2];
}tree[SZ << 2];

int mi[SZ];

void update(int p)
{
    int lch = p << 1,rch = p << 1 | 1;
    int llen = tree[lch].r - tree[lch].l + 1;
    int rlen = tree[rch].r - tree[rch].l + 1;
    tree[p].h[0] = (tree[lch].h[0] + (LL)tree[rch].h[0] * mi[llen] % mod) % mod;
    tree[p].h[1] = (tree[rch].h[1] + (LL)tree[lch].h[1] * mi[rlen] % mod) % mod;
}

void build(int p,int l,int r)
{
    tree[p].l = l; tree[p].r = r;
    if(l == r)
    {
        tree[p].h[1] = tree[p].h[0] = 0;
        return ;
    }
    int mid = l + r >> 1;
    build(p << 1,l,mid); build(p << 1 | 1,mid + 1,r);
    update(p);
}

void insert(int p,int x)
{
    if(tree[p].l == tree[p].r)
    {
        tree[p].h[1] = tree[p].h[0] = 1;
        return ;
    }
    int mid = tree[p].l + tree[p].r >> 1;
    if(x <= mid) insert(p << 1,x);
    else insert(p << 1 | 1,x);
    update(p);
}

int geth(int p,int l,int r,int d)
{
    if(l == tree[p].l && tree[p].r == r)
        return tree[p].h[d];
    int mid = tree[p].l + tree[p].r >> 1;
    if(r <= mid) return geth(p << 1,l,r,d);
    else if(l > mid) return geth(p << 1 | 1,l,r,d);
    else
    {
        int a = geth(p << 1,l,mid,d),b = geth(p << 1 | 1,mid + 1,r,d);
        int llen = mid - l + 1;
        int rlen = r - mid;     
        if(d == 0)
            return (a + (LL)b * mi[llen] % mod) % mod;
        else
            return (b + (LL)a * mi[rlen] % mod) % mod;
    }
}

void init()
{
    memset(tree,0,sizeof(tree));
}

void scanf(int &x)
{
    x = 0;
    char a = getchar();
    bool flag = 0;
    while(a < '0' || a > '9') { if(a == '-') flag = 1; a = getchar(); }
    while(a >= '0' && a <= '9') { x = x * 10 + a - '0'; a = getchar(); }
    if(flag) x = -x;
}

int main()
{
    int T;
    scanf(T);
    mi[0] = 1;
    for(int i = 1;i <= 10000;i ++)
        mi[i] = (LL)mi[i - 1] * 13331 % mod;
    while(T --)
    {
        init();
        int n;
        scanf(n);
        build(1,1,n);
        bool flag = 0;
        for(int i = 1;i <= n;i ++)
        {
            int x;
            scanf(x);
            insert(1,x);
            int len = min(x - 1,n - x);
            if(x != 1 && x != n && !flag && geth(1,x - len,x - 1,0) != geth(1,x + 1,x + len,1))
                flag = 1;
        }
        if(!flag)
            puts("N");
        else
            puts("Y");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值