P2757 [国家集训队] 等差子序列
题目描述
给一个 \(1\) 到 \(N\) 的排列 \(\{A_i\}\),询问是否存在
使得 \(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\) 中的一个。
这个在桶数组里,长成这样:

如果在桶中,所有关于 \(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;
}
本文来自博客园,作者:Trent900,转载请注明原文链接:https://www.cnblogs.com/GE9X/p/21592928

浙公网安备 33010602011771号