codevs 1283 等差子序列
Posted 日拱一卒 功不唐捐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codevs 1283 等差子序列相关的知识,希望对你有一定的参考价值。
http://codevs.cn/problem/1283/
给一个 1 到 N 的排列{Ai},询问是否存在 1<=p1<p2<p3<p4<p5<…<pLen<=N(Len>=3),使得 Ap1,Ap2,Ap3,…ApLen 是一个等差序列。
输入的第一行包含一个整数 T,表示组数。
下接 T 组数据,每组第一行一个整数 N,每组第二行为一个 1 到 N 的排列, 数字两两之间用空格隔开。
对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则输出一 行“N”。
2
3
1 3 2
3
3 2 1
N
Y
对于5%的数据,N<=100,对于30%的数据,N<=1000,对于100%的数据,N<=10000,T<=7
线段树+hash
首先要注意的是这个排列是1到n的排列
然后当然是找3个数形成等差子序列
暴力:枚举中间的数,枚举左边的数,枚举右边的数,看是否满足 2*mid=l+r
O(n³)
继续想,因为保证排列是1到n
所以对于一个数x,若以x为mid能形成等差子序列,那么另外两个数一定在x两侧
即从左往右枚举,当枚举到mid时,能早就枚举到了l,不能枚举到r
可以用0,1表示这个数是否被枚举到
举个例子:
3 6 1 2 4 5
当枚举到第5个数4时,0 1序列为
1 1 1 1 0 1
4的左边分别是1和0,说明枚举到4时,3已经被枚举到了,5还没有被枚举
但这样仍然要枚举,没有减少时间复杂度
如何去掉枚举的过程?
继续想,发现我们要比较的是mid左右的两个对称区间
举个例子:
1 8 3 6 5 7 4 2
当枚举到3时,0 1序列为:
1 0 1 0 0 0 0 1
我们实际需要的是判断2和4的01序列是否相等,1和5的01序列是否相等
因为是对称的
可以转化为判断区间[1,2]和区间[5,4](注意这里是[5,4],不是[4,5])是否相等
线段树维护区间正序哈希值和倒序哈希值,即可log判断
总复杂度:O(nlogn)
#include<cstdio> #include<cstring> #include<algorithm> #define N 10001 #define LL unsigned long long using namespace std; int T,n,x,len; bool ok; LL bit[N],hash[N*4],anti_hash[N*4],r1,r2; struct TREE { public: void up(int k,int l,int r) { hash[k]=hash[k<<1]*bit[r-(l+r>>1)]+hash[k<<1|1]; anti_hash[k]=anti_hash[k<<1|1]*bit[(l+r>>1)-l+1]+anti_hash[k<<1]; } void change(int k,int l,int r,int pos) { if(l==r) { anti_hash[k]=hash[k]=1; return; } int mid=l+r>>1; if(pos<=mid) change(k<<1,l,mid,pos); else change(k<<1|1,mid+1,r,pos); up(k,l,r); } LL query(int k,int l,int r,int opl,int opr,int w) { if(l>=opl&&r<=opr) return w==1 ? hash[k]:anti_hash[k]; int mid=l+r>>1; if(opr<=mid) return query(k<<1,l,mid,opl,opr,w); else if(opl>mid) return query(k<<1|1,mid+1,r,opl,opr,w); else if(w==1) return query(k<<1,l,mid,opl,mid,w)*bit[opr-mid]+query(k<<1|1,mid+1,r,mid+1,opr,w); else return query(k<<1|1,mid+1,r,mid+1,opr,w)*bit[mid-opl+1]+query(k<<1,l,mid,opl,mid,w); } void solve(int i) { len=min(i-1,n-i); r1=query(1,1,n,i-len,i-1,1); r2=query(1,1,n,i+1,i+len,2); if(r1!=r2) ok=true; } }tree; int main() { bit[1]=233; for(int i=2;i<N;i++) bit[i]=bit[i-1]*233; scanf("%d",&T); while(T--) { memset(hash,0,sizeof(hash)); memset(anti_hash,0,sizeof(anti_hash)); scanf("%d",&n); ok=false; for(int i=1;i<=n;i++) { scanf("%d",&x); if(!ok) { tree.change(1,1,n,x); if(x!=1&&x!=n) tree.solve(x); } } if(ok) puts("Y"); else puts("N"); } }
以上是关于codevs 1283 等差子序列的主要内容,如果未能解决你的问题,请参考以下文章
leetcode打卡——等差数列题目(LIS变式)——1218. 最长定差子序列