P2757 [国家集训队] 等差子序列

文章讨论了如何使用线段树和哈希技术在一个给定的整数序列中查找是否存在长度为3的等差子序列。通过维护数组的左右标记并检查重叠部分的对称性来确定等差子序列的存在。算法的时间复杂度为O(nlogn)。
摘要由CSDN通过智能技术生成

P2757 [国家集训队] 等差子序列

题目大意

给定一个 1 1 1 n n n的序列 a i a_i ai,询问是否存在

1 ≤ p 1 < p 2 < p 3 < ⋯ < p l e n ≤ n ( l e n ≥ 3 ) 1\leq p_1<p_2<p_3<\cdots<p_{len}\leq n \qquad (len\geq 3) 1p1<p2<p3<<plenn(len3)

使得 a p 1 , a p 2 , a p 3 , … , a p l e n a_{p_1},a_{p_2},a_{p_3},\dots,a_{p_{len}} ap1,ap2,ap3,,aplen是一个等差序列。

T T T组数据。

对于前 5 5 5个数据测试点, 1 ≤ n ≤ 5 × 1 0 5 , T ≤ 5 1\leq n\leq 5\times 10^5,T\leq 5 1n5×105,T5,时限为 5 s 5s 5s

对于后 20 20 20个数据测试点, 1 ≤ n ≤ 10000 , T ≤ 7 1\leq n\leq 10000,T\leq 7 1n10000,T7,时限为 2 s 2s 2s


题解

题意可以简化为判断这个序列是否存在一个长度为 3 3 3的等差序列。

我们考虑在什么情况下会存在一个长度为 3 3 3的等差序列。如果存在一个 a i a_i ai,使得 a i − k a_i-k aik a i + k a_i+k ai+k a i a_i ai的异侧,那么这三个数就构成等差序列。

那么我们可以按 i i i从小到大来枚举 a i a_i ai。因为 a i a_i ai是一个 1 1 1 n n n的排列,所以我们可以用一个数组 z z z来维护 a i a_i ai的值。在遍历 a i a_i ai的时候,在数组 z z z上将 a i a_i ai左侧的点标为 1 1 1,右侧的点标为 0 0 0

那么,如果不存在 k k k使得 a i − k a_i-k aik a i + k a_i+k ai+k a i a_i ai的异侧,那么 a i − k a_i-k aik a i + k a_i+k ai+k z z z值就是相同的。我们将 z z z a i a_i ai为中心对折,如果对于所有重合部分,重合的两个数都相等,那么就说明不存在 k k k使得 a i − k a_i-k aik a i + k a_i+k ai+k a i a_i ai的异侧;否则,就存在一个长度为 3 3 3的等差序列。

那我们怎么判断重合的两个数是否都相等呢?我们发现这其实就等价于重合部分左半边和右半边对称,也就是这一段是回文的。那我们只需要维护 z z z数组的正序和倒序的哈希值,判断对应段的在两部分的哈希值是否相等。如果相等就说明不存在以 a i a_i ai为中心的长度为 3 3 3的等差序列,否则就说明存在以 a i a_i ai为中心的长度为 3 3 3的等差序列。

z z z数组的正序和倒序的哈希值可以用线段树来维护。

时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

code

#include<bits/stdc++.h>
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=500000;
const long long mod=1e9+7;
int T,n,fl,a[N+5];
long long mi[N+5];
struct tree{
	long long ly[5*N+5];
	void build(int k,int l,int r){
		ly[k]=0;
		if(l==r) return;
		int mid=l+r>>1;
		build(lc,l,mid);
		build(rc,mid+1,r);
	}
	void down(int k,int l,int r){
		int mid=l+r>>1;
		ly[lc]=(ly[lc]+ly[k])%mod;
		ly[rc]=(ly[rc]+ly[k]*mi[mid+1-l])%mod;
		ly[k]=0;
	}
	void ch(int k,int l,int r,int x,int y){
		if(l>=x&&r<=y){
			ly[k]=(ly[k]+mi[l-x])%mod;
			return;
		}
		if(ly[k]) down(k,l,r);
		int mid=l+r>>1;
		if(x<=mid) ch(lc,l,mid,x,y);
		if(y>mid) ch(rc,mid+1,r,x,y);
	}
	long long find(int k,int l,int r,int x){
		if(!x) return 0;
		if(l==r&&l==x) return ly[k];
		if(ly[k]) down(k,l,r);
		int mid=l+r>>1;
		if(x<=mid) return find(lc,l,mid,x);
		else return find(rc,mid+1,r,x);
	}
	long long gt(int l,int r){
		return (find(1,1,n,r)-find(1,1,n,l-1)*mi[r-l+1]%mod+mod)%mod;
	}
}T1,T2;
int main()
{
	mi[0]=1;
	for(int i=1;i<=N;i++) mi[i]=mi[i-1]*2%mod;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		T1.build(1,1,n);
		T2.build(1,1,n);
		fl=0;
		for(int i=2;i<=n;i++){
			T1.ch(1,1,n,a[i-1],n);
			T2.ch(1,1,n,n-a[i-1]+1,n);
			int tmp=min(a[i]-1,n-a[i]);
			int l=a[i]-tmp,r=a[i]+tmp;
			if(T1.gt(l,r)!=T2.gt(n-r+1,n-l+1)){
				fl=1;break;
			}
		}
		if(fl) printf("Y\n");
		else printf("N\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值