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

题目描述

给一个 \(1\)\(N\) 的排列 \(\{A_i\}\),询问是否存在

\[1 \le p_1<p_2<p_3<p_4<p_5<…<p_{Len} \le N (Len \ge 3) \]

使得 \(Ap_1,Ap_2,Ap_3,\cdots,Ap_{Len}\) 是一个等差序列。

输入格式

输入的第一行包含一个整数 \(T\),表示组数。

下接 \(T\) 组数据,每组第一行一个整数 \(N\),每组第二行为一个 \(1\)\(N\) 的排列,数字两两之间用空格隔开。

输出格式

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

输入输出样例 #1

输入 #1

2
3
1 3 2
3
3 2 1

输出 #1

N
Y

说明/提示

对于前 \(5\) 个测试点的数据,\(1 \leq N \leq 5\times 10^5,T \leq 5\),时限 5s。

对于后 \(21\) 个测试点的数据,\(1 \leq N \leq10000,T\leq 7\),时限 2s。

题意简述

简单来说,就是给一个排列,询问是否存在一个等差三元组。因为等差的三元组其实和等差数列等价。

解题思路

我们利用排列的性质转化问题:尝试从左到右枚举中项,并开一个桶数组 \(b\),其中 \(b_i\) 代表中项枚举到 \(x\) 时,\(i\) 是否在 \(a_{[1, x)}\) 中出现过。

当我们确定了中间项 \(a_x\) 以及公差 \(k\),如果这个三元组成立,就代表 \(a_i\) 之前,即桶数组 \(b\) 中,只能出现 \(a_x + k\)\(a_x - k\) 中的一个。

这个在桶数组里,长成这样:

image

如果在桶中,所有关于 \(a_x\) 对称的数对,要么全部出现了,要么全部没出现。那么就说明,这些数对全部在 \(a_x\) 的一侧,不可能和 \(a_x\) 构成等差三元组。

因此,如果在枚举到 \(a_x\) 时,桶数组以 \(a_x\) 为中心的最长子串是回文的,就说明没有以 \(a_x\) 为中项的三元组了。

如此,我们只需要维护 \(b\) 的两个功能:

  • 快速单点修改。

  • 快速查询子串是否回文。

可以使用线段树上维护字符串哈希解决。

代码

小清新。

// Problem: P2757 [国家集训队] 等差子序列
// Contest: Luogu
// Memory Limit: 512 MB
// Time Limit: 5000 ms

#include <bits/stdc++.h>
#define int unsigned long long
#define Misaka namespace
#define Network std
using Misaka Network;
const int N = 5e5 + 7, B = 1031;
int n, a[N], pw[N];

struct SGT{
	int val[N << 2];
	#define ls (x << 1)
	#define rs ((x << 1) | 1)
	#define mid ((l + r) >> 1)
	void build(int x, int l, int r){
		val[x] = 0;
		if(l == r) return;
		build(ls, l, mid), build(rs, mid + 1, r);
	}
	void Mdf(int x, int l, int r, int q, int k){
		if(l == r) return val[x] = k * pw[q], void();
		if(q <= mid) Mdf(ls, l, mid, q, k);
		else Mdf(rs, mid + 1, r, q, k);
		val[x] = val[ls] + val[rs]; 
	}
	int sum(int x, int l, int r, int ql, int qr){
		if(ql <= l && r <= qr) return val[x];
		int res = 0;
		if(ql <= mid) res += sum(ls, l, mid, ql, qr);
		if(mid < qr)  res += sum(rs, mid + 1, r, ql, qr);
		return res;
	}
} f, r;

void solve(){
	
	cin >> n;
	f.build(1, 1, n), r.build(1, 1, n);
	
	for(int i = 1; i <= n; i ++) cin >> a[i];
	
	for(int i = 1; i <= n; i ++){
		int sp = min(a[i] - 1, n - a[i]);
		int ql = a[i] - sp, qr = a[i] + sp;
		int fr = f.sum(1, 1, n, ql, qr);
		int re = r.sum(1, 1, n, n - qr + 1, n - ql + 1);
		
		if(ql <= n - qr + 1) fr *= pw[n - qr + 1 - ql];
		else re *= pw[ql - (n - qr + 1)];
		
		if(fr != re) return cout << "Y\n", void();
		
		f.Mdf(1, 1, n, a[i], 1);
		r.Mdf(1, 1, n, n - a[i] + 1, 1);
	}
	cout << "N\n";
	
}

signed main(){
	ios::sync_with_stdio(0), cin.tie(0);
	
	pw[0] = 1;
	for(int i = 1; i <= 500000; i ++) pw[i] = pw[i - 1] * 1523;
	
	int t; cin >> t;
	while(t --) solve();
	return 0;
}
posted @ 2026-07-17 20:50  Trent900  阅读(11)  评论(0)    收藏  举报