[BZOJ2124]等差子序列(hash)

题意

给出n个数的一个全排列(n<=1e5),询问是否存在三个数,使得这三个数成等差数列,多组询问

思路

枚举中间的数,显然在值域上两边的数成对称,那么如果存在等差数列,一定有一个比它小的数在左边,一个大的在右边,而且关于这个中间的数对称。换句话说,两个数的值如果关于该数对称,那么他们的位置就不能同时出现在该数的左边或者右边

假设有5个数的全排列2 3 1 5 4,我们把在当前数左边设为1,右边设为0(当前数随便在左边或者右边)

举个栗子:给定数列2 3 5 1 4

依次加入每个数

1.( 0 , 0, 0 , 0 , 0 )//初始状态

2.( 0 , 1, 0 , 0 , 0)//加入2

3.( 0 ,1 , 1 , 0 , 0 )//加入3,可以发现在原串中存在一个以3为中心的等差序列( 2 , 3 , 4)

结合上面对左右关系的讨论,可以发现此时在第三步加入3后,以3为中心左右不对称,这就意味着左边有个比3小的数,右边有个比它大的数,所以存在等差序列

于是原问题转化为求回文串,用hash同时维护对应顺着扫和逆着扫,查询就看对应的区间是否相等,不相等就说明有回文串

单点修改怎么办?线段树维护hash就行啦

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 10005
using namespace std;
typedef unsigned long long ULL;
const ULL base=1000000007;
int T,n,a[N];
ULL hash1[N<<2],hash2[N<<2],temp[N];

template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign; 
}
void init()
{
	read(n);
	for(int i=1;i<=n;++i) read(a[i]);
	memset(hash1,0,sizeof(hash1));
	memset(hash2,0,sizeof(hash2));
}
void modify(ULL *hash,int rt,int l,int r,int x,int val)
{
	if(l==r)
	{
		hash[rt]=val;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) modify(hash,rt<<1,l,mid,x,val);
	else modify(hash,rt<<1|1,mid+1,r,x,val);
	hash[rt]=hash[rt<<1]*temp[r-mid]+hash[rt<<1|1]; 
}
ULL query(ULL hash[],int rt,int l,int r,int x,int y)
{
	if(x<=l&&r<=y) return hash[rt];
	int mid=(l+r)>>1;
	if(y<=mid) return query(hash,rt<<1,l,mid,x,y);
	if(x>mid) return query(hash,rt<<1|1,mid+1,r,x,y);
	ULL h1=query(hash,rt<<1,l,mid,x,y),h2=query(hash,rt<<1|1,mid+1,r,x,y);
	return h1*temp[min(r,y)-mid]+h2;//注意这里的min(r,y)-mid表示右边的size 
}
int main()
{
	read(T);
	temp[0]=1;
	for(int i=1;i<N;++i) temp[i]=temp[i-1]*base;
	while(T--)
	{
		init();
		for(int i=1;i<=n;++i)
		{
			int p=min(n-a[i],a[i]-1);
			if(p&&query(hash1,1,1,n,a[i]-p,a[i]+p)!=query(hash2,1,1,n,(n-a[i]+1)-p,(n-a[i]+1)+p))
			{
				printf("Y\n");
				goto u;
			}
			modify(hash1,1,1,n,a[i],1);
			modify(hash2,1,1,n,n-a[i]+1,1);
		}
		printf("N\n");
		u:;
	}
	return 0;
}
posted @ 2019-07-16 13:04  擅长平地摔的艾拉酱  阅读(181)  评论(0编辑  收藏  举报
/*取消选中*/