BZOJ2124 等差子序列(树状数组+哈希)
容易想到一种暴力的做法:枚举中间的位置,设该位置权值为x,如果其两边存在权值关于x对称即合法。
问题是如何快速寻找这个东西是否存在。考虑仅将该位置左边出现的权值标1。那么若在值域上若关于x对称的两权值标号不同,说明他们的位置分别在两侧,也就说明存在等差子序列。那么只需要判断整体是否相同,哈希即可。
哈希值需要动态维护,容易想到树状数组/线段树。从左到右依次处理并维护两个树状数组记录正反哈希值。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 10010 #define ul unsigned long long int T,n,a[N]; ul tree[N],tree2[N],p[N]; void add(int k){ul x=p[k-1];while (k<=n) tree[k]+=x,k+=k&-k;} void add2(int k){ul x=p[n-k];while (k) tree2[k]+=x,k-=k&-k;} ul query(int k){ul s=0;while (k) s+=tree[k],k-=k&-k;return s;} ul query2(int k){ul s=0;while (k<=n) s+=tree2[k],k+=k&-k;return s;} int main() { #ifndef ONLINE_JUDGE freopen("bzoj2124.in","r",stdin); freopen("bzoj2124.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif T=read(); while (T--) { n=read(); for (int i=1;i<=n;i++) a[i]=read(); p[0]=1;for (int i=1;i<=n;i++) p[i]=p[i-1]*107; memset(tree,0,sizeof(tree)); memset(tree2,0,sizeof(tree2)); bool flag=0; for (int i=1;i<=n;i++) { if (a[i]-1<n-a[i]) { if (query(a[i]-1)*p[n-(a[i]<<1)+1]!=query2(a[i]+1)-query2(a[i]<<1)) {flag=1;break;} } else { if (query2(a[i]+1)*p[a[i]-(n-a[i])-1]!=query(a[i]-1)-query(a[i]-(n-a[i])-1)) {flag=1;break;} } add(a[i]);add2(a[i]); } if (flag) printf("Y\n");else printf("N\n"); } return 0; }