BZOJ 2124 等差子序列(线段树)

Description
给一个 1 1 N的排列 Ai A i ,询问是否存在 1p1<p2<p3<p4<p5<<pLenN 1 ≤ p 1 < p 2 < p 3 < p 4 < p 5 < ⋯ < p L e n ≤ N (3len) ( 3 ≤ l e n ) ,使得 Ap1,Ap2,Ap3,,ApLen A p 1 , A p 2 , A p 3 , ⋯ , A p L e n 是一个等差序列

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

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

Sample Input
2
3
1 3 2
3
3 2 1

Sample Output
N
Y

Data Size & Hint
N10000,T7

Solution

要求一个 len3 l e n ≥ 3 的等差子序列,那还不如直接求 len=3 l e n = 3 的等差子序列

考虑等差子序列的性质 a1+a3=2a2 a 1 + a 3 = 2 a 2
设中项为 x x ,易得左侧有一点x+d,右侧有一点 xd x − d , 1<abs(d)<x 1 < a b s ( d ) < x

如果 x+d,xd x + d , x − d 没有出现在 x x 的同一侧,那么显然的,必然会出现len=3的等差子序列
考虑维护一侧的数(没有出现的就在另一侧)

当前询问到第 i i 个位置,为pi
1n 1 − n 数轴上标记已经出现过的数
每次从数轴上点 pi p i 开始向两端扩展 (l=r=i,l,r++) ( l = r = i , l − − , r + + )

pi p i 为中心两端对称分布 (00,11) ( 0 − 0 , 1 − 1 ) (忽略多余部分),则说明 pi+d,pid p i + d , p i − d 都出现在 pi p i 的同一侧(全0即右侧,全1即左侧),不存在以 i i 位置为中心的等差子序列
否则存在以i位置为中心的等差子序列

考虑如何维护左右部分是否对称

要求:单点修改,区间求和
树状数组或线段树即可

开两个同时维护正向与反向,每次提取一段对比即可
lenth=min(na[i],a[i]1); l e n t h = m i n ( n − a [ i ] , a [ i ] − 1 ) ;
正向区间 a[i],a[i]+lenth a [ i ] , a [ i ] + l e n t h
反向区间 na[i]+1,na[i]+1+lenth n − a [ i ] + 1 , n − a [ i ] + 1 + l e n t h
若相等,则不存;若不等,则存在

单点修改线段树直接维护就行
记得update比较特殊,需要乘上一个相对位置

具体看代码.

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define L (x<<1)
#define R (x<<1)+1
#define mid ((l+r)>>1)
const int N = 100010;
const ll mod = 19260817;
int n,a[N];
ll p[N];
struct tree {
    ll node[N<<4],siz[N];
    void clear() {
        memset(node,0,sizeof(node));
        memset(siz,0,sizeof(siz));
    }
    void update(int x) {node[x]=(node[L]*p[siz[R]]%mod+node[R])%mod;}
    void build(int x,int l,int r) {
        if(l!=r) {
            build(L,l,mid);
            build(R,mid+1,r);
            siz[x]=siz[L]+siz[R];
        }
        else siz[x]=1;
    }
    void change(int x,int p,int l,int r) {
        if(l==r) {node[x]=1;return;}
        if(p<=mid) change(L,p,l,mid);
        else change(R,p,mid+1,r);
        update(x);
    }
    ll query(int x,int l,int r,int _l,int _r) {
        if(l>=_l && r<=_r) {return node[x]*p[_r-r]%mod;}
        ll ans=0;
        if(mid>=_l) ans+=query(L,l,mid,_l,_r);
        if(mid<_r) ans+=query(R,mid+1,r,_l,_r);
        ans%=mod;
        return ans;
    }
}t1,t2;
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') {flag=-1;ch=getchar();}
    while(ch>='0' && ch<='9') {ans=ans*10+ch-'0';ch=getchar();}
    return ans*flag;
}
void init() {
    n=read();p[0]=1;
    t1.clear();t2.clear();
    t1.build(1,1,n);t2.build(1,1,n);
    for(int i=1;i<=n;++i) {
        a[i]=read();
        p[i]=p[i-1]*2%mod;
    }
}
void work() {
    bool flag=0;
    for(int i=1;i<=n;++i) {
        t1.change(1,a[i],1,n);
        t2.change(1,n-a[i]+1,1,n);
        int lenth=min(n-a[i],a[i]-1);
        if(lenth && t1.query(1,1,n,a[i],a[i]+lenth) != t2.query(1,1,n,n-a[i]+1,n-a[i]+1+lenth)) {
            puts("Y");
            flag=1;
            break;
        }
    }
    if(!flag) puts("N");
}
int main() {
    int t=read();
    while(t--) {
        init();
        work();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值