2124: 等差子序列

Time Limit: 3 Sec Memory Limit: 259 MB
Submit: 2777 Solved: 977
[Submit][Status][Discuss]

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


这道题,直观地,我们可以发现,长度大于等于3的子序列,实际上我们只需要找长度为3的等差子序列就可以了,因为长度大于3的等差子序列,必然可以分解为长度为3的子序列。

然后还有重要的一点就是,这是一个排列。所以每个数字只出现一次。

然后我们考虑枚举每一个数字 j ,看是否存在 i<j<k && i+k=j*2 即可。然后需要看的数字特别多,我们不能一个一个枚举,必然TLE。 我们必须快速枚举 所有数字,我们可以发现,对于一个表示数字是否出现过的0/1串,只有当他们相等时,代表数字都在这个数字之前出现了,或者都在这个数字之后出现。

相当于,我们就是按照数字枚举,把出现过的放到权值线段树中,然后对于每一对数字,要么都出现,要么都未出现,这就是不行的。否则可以。

于是我们可以用线段树维护hash值,然后直接看hash值是否相等,就能知道这段序列是否相等了。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+10,mod=1e9+7;//hash
int T,n,a[N],flag,base[N];
struct node{int l,r,h1,h2;}t[N<<2];
inline void push_up(int p){
	int l1=t[p<<1].r-t[p<<1].l+1;
	int l2=t[p<<1|1].r-t[p<<1|1].l+1;
	t[p].h1=(t[p<<1].h1+t[p<<1|1].h1*base[l1]%mod)%mod;
	t[p].h2=(t[p<<1].h2*base[l2]%mod+t[p<<1|1].h2)%mod;
}
void build(int p,int l,int r){
	t[p].l=l; t[p].r=r; t[p].h1=t[p].h2=0;
	if(l==r)	return ;	int mid=l+r>>1;
	build(p<<1,l,mid);	build(p<<1|1,mid+1,r);
}
void change(int p,int x){
	if(t[p].l==t[p].r)	return void(t[p].h1=t[p].h2=1);
	int mid=t[p].l+t[p].r>>1;
	if(x<=mid)	change(p<<1,x);	
	else	change(p<<1|1,x);
	push_up(p);
}
int ask1(int p,int l,int r){
	if(t[p].l==l&&t[p].r==r)	return t[p].h1;
	int mid=t[p].l+t[p].r>>1;
	if(r<=mid)	return ask1(p<<1,l,r);
	else if(l>mid)	return ask1(p<<1|1,l,r);
	else	return (ask1(p<<1,l,mid)+ask1(p<<1|1,mid+1,r)*base[mid-l+1]%mod)%mod;
}
int ask2(int p,int l,int r){
	if(t[p].l==l&&t[p].r==r)	return t[p].h2;
	int mid=t[p].l+t[p].r>>1;
	if(r<=mid)	return ask2(p<<1,l,r);
	else if(l>mid)	return ask2(p<<1|1,l,r);
	else	return (ask2(p<<1,l,mid)*base[r-mid]%mod+ask2(p<<1|1,mid+1,r))%mod;
}
signed main(){
	scanf("%lld",&T); base[0]=1;
	for(int i=1;i<=10000;i++)	base[i]=base[i-1]*13331%mod; 
	while(T--){
		scanf("%lld",&n); flag=0; build(1,1,n);
		for(int i=1;i<=n;i++)	scanf("%lld",&a[i]);
		for(int i=1;i<=n&&!flag;i++){
			int len=min(a[i]-1,n-a[i]);
			if(a[i]!=1&&a[i]!=n&&ask1(1,a[i]-len,a[i]-1)!=ask2(1,a[i]+1,a[i]+len))	
				flag++;
			change(1,a[i]); 
		}
		puts(flag?"Y":"N");
	}
	return 0;
}