题目描述: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; }