BZOJ 2124:等差子序列

简要题意:

给定一个长度为n的排列pi,问是否存在一个长度大于等于3的等差子序列。

思路:

首先,很显然的一点,我们只需要考虑是否存在长度等于3的等差子序列即可。

而且要注意到本题非常特殊的一点是排列,这意味着[1..n]中的每个数都恰好出现一次且仅出现一次。

那么当我们枚举等差子序列中间的那一项时,不妨设为a[mid],

那么假设等差子序列的第一项为a[l](l<mid),那么如果2*a[mid]-a[l]>=1 且 2*a[mid]-a[l]<=n,

那么2*a[mid]-a[l]要么落在mid左侧,要么落在mid右侧。

如果落在mid右侧,那么长度等于3的等差子序列即存在。

由于时限的关系,O(n^2)的做法会超时,因此我们显然不能在枚举中间项的同时再枚举第一项;

因此此时优化的点在于不枚举第一项,而是采用一种替代的方法,不通过枚举来得到我们需要的效果。

我们需要用一种数据结构来存储在mid左侧出现过的数字。

并且支持快速查找每一个[1..n]的数字x,2*a[mid]-x是否也同时在这个数据结构中出现。

如果2*a[mid]-x不在这个数据结构出现,且1<=2*a[mid]-x<=n,说明2*a[mid]-x一定在mid的右侧出现,那么说明此时我们需要的等差子序列出现了。

这启发我们用权值线段树来完成这个工作,

我们可以用一个01权值线段树来记录所有出现在mid左侧的数字,即x这个数值出现,则线段树上x的位置的值为1,否则为0;

那么相当于我们需要判断的是我们的01权值线段树是否关于a[mid]值对称。(当然细节需要注意,超出[1..n]的部分需要丢掉)

快速判断两个区间是否相同,这就属于字符串hash的功能范围内了。

因此,我们在权值线段树上同时维护区间从左到右的Hash值,从右到左的Hash值,当查询的时候只需要快速判断一下a[mid]两侧的Hash值是否相同即可。

如果不同,说明存在我们需要的等差子序列。

当我们移动mid时,对权值线段树做的修改就是单点修改,比较方便维护两个Hash值的变化。

至此,本题已经完成。时间复杂度为O(NlogN)。

#include<bits/stdc++.h>
#define lson(p) p<<1
#define rson(p) p<<1|1
#define rep(i,a,b) for (int i=a;i<b;i++)
#define per(i,a,b) for (int i=a-1;i>=b;i--)
#define all(v) (v).begin() (v).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define pb push_back
#define mp make_pair
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
typedef vector<int> VI;
clock_t startTime;
mt19937 mrand(random_device{}());
const int mod=1e9+7;
const int maxn=1e4+10;
int pw[maxn];
int a[maxn];
int T,n;
db getCurrentTime()
{
    return (db)(clock()-startTime)/CLOCKS_PER_SEC;
}
struct node
{
    int Lhash,Rhash;
    int len;
    int v;
}b[maxn<<2];
void pushup(int p)
{
    b[p].Lhash=(1LL*b[lson(p)].Lhash*pw[b[rson(p)].len]%mod+b[rson(p)].Lhash)%mod;
    b[p].Rhash=(1LL*b[rson(p)].Rhash*pw[b[lson(p)].len]%mod+b[lson(p)].Rhash)%mod;
    b[p].len=b[lson(p)].len+b[rson(p)].len;
}
void build(int p,int l,int r)
{
    if (l==r) {
        b[p].len=1;
        b[p].Lhash=b[p].Rhash=0;
        b[p].v=0;
        return ;
    }
    int mid=(l+r)>>1;
    build(lson(p),l,mid);
    build(rson(p),mid+1,r);
    pushup(p);
}
void modify(int p,int l,int r,int pos)
{
    if (l==r) {
        b[p].v=1;
        b[p].Lhash=b[p].Rhash=1;
        return ;
    }
    int mid=(l+r)>>1;
    if (pos<=mid) modify(lson(p),l,mid,pos);
    else modify(rson(p),mid+1,r,pos);
    pushup(p);
}
node getHash(int p,int l,int r,int L,int R)
{
    if (L>R) {
        node e;
        e.Lhash=e.Rhash=0;
        return e;
    }
    if (L<=l&&r<=R) return b[p];
    int mid=(l+r)>>1;
    if (R<=mid) return getHash(lson(p),l,mid,L,R);
    else if (L>mid) return getHash(rson(p),mid+1,r,L,R);
    else {
        node e1=getHash(lson(p),l,mid,L,R);
        node e2=getHash(rson(p),mid+1,r,L,R);
        node e;
        e.Lhash=(1LL*e1.Lhash*pw[e2.len]%mod+e2.Lhash)%mod;
        e.Rhash=(1LL*e2.Rhash*pw[e1.len]%mod+e1.Rhash)%mod;
        e.len=e1.len+e2.len;
        return e;
    }
}
int main()
{
    pw[0]=1;
    for (int i=1;i<maxn;i++) pw[i]=1LL*pw[i-1]*233%mod;
    scanf("%d",&T);
    while (T--) {
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        build(1,1,n);
        bool bo=false;
        for (int i=1;i<=n;i++) {
            int x=min(a[i]-1,n-a[i]);
            node l=getHash(1,1,n,a[i]-x,a[i]-1);
            node r=getHash(1,1,n,a[i]+1,a[i]+x);
            if (l.Rhash!=r.Lhash) {
                bo=true; break;
            }
            modify(1,1,n,a[i]);
        }
        if (bo) printf("Y
");
        else printf("N
");
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/xyw5vplus1/p/14586343.html