【BZOJ2124】等差子序列 树状数组维护hash值

【BZOJ2124】等差子序列

Description

给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pLen<=N (Len>=3),
使得Ap1,Ap2,Ap3,…ApLen是一个等差序列。

Input

输入的第一行包含一个整数T,表示组数。
下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开。
N<=10000,T<=7

Output

对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则输出一行“N”。

Sample Input

2
3
1 3 2
3
3 2 1

Sample Output

N
Y

题解:显然我们只要让len=3就行。考虑枚举中间数,用桶维护之前已经出现过的数。我们将桶看成一个01串,如果当前数是v[i],考虑桶中v[i]左右两边的串。如果存在v[i]-j和v[i]+j在桶中的存在情况不同,则表明找到了一个等差子序列。即,如果不存在等差子序列,则桶中v[i]的左右两边一定是对称的。用树状数组维护桶的正反hash值即可。

我才不会告诉你这题直接用bitset就能过呢~

 

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=1000000009;
const int maxn=10010;
int n;
int v[maxn];
ll bs[maxn];
struct node
{
	ll s[maxn];
	inline void updata(int x,ll val)
	{
		for(int i=x;i<=n;i+=i&-i)	s[i]=(s[i]+val)%P;
	}
	inline ll query(int x)
	{
		ll ret=0;
		for(int i=x;i;i-=i&-i)	ret=(ret+s[i])%P;
		return ret;
	}
}s1,s2;
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
void work()
{
	n=rd();
	int i,a;
	for(i=1;i<=n;i++)	v[i]=rd();
	memset(s1.s,0,sizeof(s1.s)),memset(s2.s,0,sizeof(s2.s));
	for(bs[0]=1,i=1;i<=n;i++)	bs[i]=bs[i-1]*131%P;
	for(i=1;i<=n;i++)
	{
		a=min(v[i]-1,n-v[i]);
		if((s1.query(v[i]+a)-s1.query(v[i])+P)*bs[n-v[i]+1]%P!=(s2.query(n-v[i]+1+a)-s2.query(n-v[i]+1)+P)*bs[v[i]]%P)
		{
			printf("Y\n");
			return ;
		}
		s1.updata(v[i],bs[v[i]]),s2.updata(n-v[i]+1,bs[n-v[i]+1]);
	}
	printf("N\n");
}
int main()
{
	int T=rd();
	while(T--)	work();
	return 0;
}//2 3 1 3 2 3 3 2 1 

 

 
posted @ 2017-10-15 11:48  CQzhangyu  阅读(381)  评论(0编辑  收藏  举报