Codeforces 452F Permutation【线段树】【哈希】

Description

You are given a permutation of numbers from 1 to n. Determine whether there’s a pair of integers a, b (1 ≤ a, b ≤ n; a ≠ b) such that the element img (note, that it is usual division, not integer one) is between a and b in this permutation.

题解

加入了一个 a[i] ,怎样才会出现等差数列?显然,如果出现了等差数列,一定存在这样的一个 k ,使得a[i]k a[i]+k 不在 a[i] 的同一侧,那么我们只要维护一个权值线段树,每次插入一个权值,只要它在权值线段树中的左右两边(属于[1,n]的部分)是对称的就不会出现等差数列了(如果不对称,说明一定有一个没出现过一个出现过了,那么就会出现等差数列)。怎样判断左右是否对称?用线段树维护区间哈希值即可。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 300006
#define tt 998244353
#define LL long long
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
struct data{
    int x,l,r;
    LL suml,sumr;
}tree[maxn*4];
int n,a[maxn],P[maxn];
void build(int p,int l,int r){
    tree[p].l=l;tree[p].r=r;
    if(l>=r)return;
    int mid=(l+r)>>1;
    build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
void update(int p,int x){
    if(x<tree[p].l||x>tree[p].r)return;
    if(tree[p].l==tree[p].r){
        tree[p].x=1;tree[p].suml=tree[p].sumr=1;
        return;
    }
    update(p<<1,x);update(p<<1|1,x);
    tree[p].suml=(tree[p<<1].suml*P[tree[p<<1|1].r-tree[p<<1|1].l+1]%tt+tree[p<<1|1].suml)%tt;
    tree[p].sumr=(tree[p<<1|1].sumr*P[tree[p<<1].r-tree[p<<1].l+1]%tt+tree[p<<1].sumr)%tt;
}
LL queryl(int p,int l,int r){
    if(l>tree[p].r||r<tree[p].l)return 0;
    if(l<=tree[p].l&&r>=tree[p].r)return tree[p].suml%tt;
    if(tree[p<<1].r>=l&&tree[p<<1|1].l<=r)return (queryl(p<<1,l,tree[p<<1].r)*P[r-tree[p<<1|1].l+1]%tt+queryl(p<<1|1,tree[p<<1|1].l,r))%tt;else
    if(tree[p<<1].r>=r)return queryl(p<<1,l,r);else
                       return queryl(p<<1|1,l,r);
}
LL queryr(int p,int l,int r){
    if(l>tree[p].r||r<tree[p].l)return 0;
    if(l<=tree[p].l&&r>=tree[p].r)return tree[p].sumr%tt;
    if(tree[p<<1].r>=l&&tree[p<<1|1].l<=r)return (queryr(p<<1|1,tree[p<<1|1].l,r)*P[tree[p<<1].r-l+1]%tt+queryr(p<<1,l,tree[p<<1].r))%tt;else 
    if(tree[p<<1].r>=r)return queryr(p<<1,l,r)%tt;else
                       return queryr(p<<1|1,l,r)%tt;
}
int main(){
    n=_read();
    build(1,1,n);
    P[0]=1;
    for(int i=1;i<=n;i++)P[i]=P[i-1]*2%tt;
    for(int i=1;i<=n;i++){
        a[i]=_read();
        int l=max(1,2*a[i]-n),r=min(n,a[i]*2-1);
        if((a[i]!=1)&&(a[i]!=n)&&((queryl(1,l,a[i]-1)^queryr(1,a[i]+1,r))!=0))return (printf("YES\n"),0);
        update(1,a[i]);
    }
    printf("NO\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值