题目描述:https://www.luogu.org/problemnew/show/P2757

 

解析:首先一个显然的性质是如果有一个长度大于等于3的等差子序列那么就一定有一个长度为3的等差子序列,反之如果没有一个长度为3的等差子序列,那么原序列就没有等差子序列。那么现在的问题就转化为了求一个数列中是否有一个长度为3的等差子序列。为了方便表述,后面将长度为3的等差子序列简称为等差数列。由于是3个数,很容易就想到枚举中间位置(其实我没想到)。由于是排列,所以原数列没有重复的数。考虑将中间位置左边的数全部染色成0,中间位置右边的数全部染色成1。发现如果要判断这个中间位置的数能否成为一个等差数列的中项,一定满足以这个数为中心的最长的01串(按权值排序后)一定不是回文的。例如原数列是 5 3 2 1 4,现在mid位置枚举到了3,权值为2,那么这时 01串便是0 0 1 1 1,按权值排序后便变成了1 1 0 1 0。现在要判断 2是否为一个等差数列的中项, 只需要看以2为中心最长的区间(1,3)是否为回文串。显然不是,所以找到了一个等差数列。证明简略证一下:一个以这个数n为中心的等差数列它的第3项一定不超过2*(n-1),因为最小的第一项就是1,最大也超不过2*(n-1)。对于一段区间,如果它不是回文串,即中间右边的数与左边的不一样,即在原数列中不在同一侧,它们3个就组成了一个等差数列。于是原问题转化为了如何快速判断一个字符串是否是回文串,支持修改操作。发现一个会问串正着进行一次Hash和反着进行一次Hash结果是相同的,很容易就想到了用线段树维护Hash值。注意下细节就ok了

附上代码:

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

typedef unsigned long long ULL;
const int Base=1331,MAXN=100005;
int T,n;
ULL powpow[MAXN];
int a[MAXN];
int root=1;
int lson[2*MAXN],rson[2*MAXN];
int ll[2*MAXN],rr[2*MAXN];
ULL sum[2*MAXN][2];//zheng fan
int ndnum=0;
struct pii{
    ULL first,second;
    int l,r;
};

void prework(){
    powpow[0]=1;
    for(int i=1;i<MAXN;i++)
    powpow[i]=powpow[i-1]*Base;
}

void update(int t){
    sum[t][0]=sum[lson[t]][0]*powpow[rr[rson[t]]-ll[rson[t]]+1]+sum[rson[t]][0];
    sum[t][1]=sum[rson[t]][1]*powpow[rr[lson[t]]-ll[lson[t]]+1]+sum[lson[t]][1];
    ll[t]=ll[lson[t]];rr[t]=rr[rson[t]];
}

void buildtree(int &t,int l,int r){
    t=++ndnum;ll[t]=l;rr[t]=r;
    if(l==r){
        sum[t][0]=sum[t][1]=1;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(lson[t],l,mid);buildtree(rson[t],mid+1,r);
    update(t);
}

void modify(int t,int l,int r,int L,int R){
    if(l==r){
        sum[t][0]=sum[t][1]=0;
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) modify(lson[t],l,mid,L,R);
    else modify(rson[t],mid+1,r,L,R);
    update(t);
}

pii get_pii(pii a,pii b){
    pii res;
    res.first=a.first*powpow[b.r-b.l+1]+b.first;
    res.second=b.second*powpow[a.r-a.l+1]+a.second;
    res.l=a.l;res.r=b.r;
    return res;
}

pii query(int t,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        pii res;
        res.first=sum[t][0];res.second=sum[t][1];
        res.l=l;res.r=r;
        return res;
    } 
    int mid=(l+r)>>1;
    pii lf,rt;
    if(L<=mid)
    lf=query(lson[t],l,mid,L,R);
    if(R>mid)
    rt=query(rson[t],mid+1,r,L,R);
    if(R<=mid) return lf;
    else if(L>mid) return rt; 
    else return get_pii(lf,rt);
}

void work(){
    buildtree(root,1,n);
    modify(root,1,n,a[1],a[1]);
    bool flag=false;
    for(int mid=2;mid<=n;mid++){
        int L=max(1,2*a[mid]-n),R=min(n,2*a[mid]-1);
        pii res=query(root,1,n,L,R);
        if(a[mid]!=1&&a[mid]!=n&&res.first!=res.second){
            flag=true;
            break;
        } 
        modify(root,1,n,a[mid],a[mid]);
    }
    if(flag) printf("Y\n");
    else printf("N\n");
}

int main(){
    prework();
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)    
        scanf("%d",&a[i]);
        work();
    }
    return 0;
}
View Code