P2757 [国家集训队]等差子序列
题目链接
https://www.luogu.com.cn/problem/P2757
题目大意
给你一个 1 ~ N 的排列,问是否存在等差子序列
解题思路
权值线段树 + hash
首先要满足等差序列的条件为 a[ i ] + a[ k ] = 2 * a[ j ] ,其中 i < j < k
那么根据这个条件我们就可以枚举等差序列中心,判断两端是否有满足条件的 a[i] , a[k]
我们可以知道若一个排列长度为 3 , 并且我们要以 2 为等差序列中心 , 那么 1 3 就必须在 2 的两端
理解以上后,我们可以选择枚举等差序列的中心 j ,来找它的两端是否有满足条件的 i , k
假设现在有个数组 vis , 其中 vis[i] 表示 i 这个数是否已经出现
举个栗子 , 假设现在序列为 1 2 3 , 那么我们枚举到 a[1] 时vis[1] = 1 , vis[2] = 0 , vis[3] = 0
当我们枚举到 a[2] 时 , vis[2] = 1 , vis[1] = 1 , vis[3] = 0,枚举到 a[3] 的时全都等于1
其中,枚举到 a[2] 即枚举到 2 这个数的时候 , vis[1] = 1 , vis[3] = 0 , 数组呈现为 110
也就是说 1 这个数是在 2 之前出现的 , 3这个数是在 2 之后出现的,那么我们就可以以
a[2] 为中心构造等差子序列;而如果枚举到2时 1、3 都在2的同一端即 111 或者 010
那么就无法以a[2]为中心构造等差子序列
根据以上我们会发现当以 X 这个数为中心时,如果两端对称位置数的数组值相同
则说明这对称位置的数同时存在于 X 的同一侧,也就是它们和 X 无法构成等差子序列
那如果对称位置数组值不相同,则它们存在 X 的不同侧,也就是它们可以和 X 构成等差子序列
那么倘若 X 两端对称位置数的数组值全相同,则以 X 为中心的数组呈现会是一个回文串(左右两端相同)
所以我们只要枚举每个数,每次枚举判断这个数作为中心是否会产生回文串 , 待找到不回文或者枚举结束即可
至于回文我们可以用线段树来维护正反hash值来操作
AC_Coder
#include<bits/stdc++.h> #define rep(i,a,n) for (int i=a;i<=n;i++) #define per(i,n,a) for (int i=n;i>=a;i--) #define int long long #define ll long long #define ull unsigned long long using namespace std; const int P = 13331 , mod = 999998639; const int N = 2e5 + 10; ull power[N]; int a[N]; struct Seg_ment{ int l , r ; ull pre , suf; }tree[N << 2]; void push_up(int id , int len1 , int len2) { tree[id].pre = tree[id << 1].pre * power[len2] + tree[id << 1 | 1].pre; tree[id].pre %= mod; tree[id].suf = tree[id << 1 | 1].suf * power[len1] + tree[id << 1].suf; tree[id].suf %= mod; } void build(int id , int l , int r) { tree[id].pre = tree[id].suf = 0; tree[id].l = l , tree[id].r = r; if(l == r) return ; int mid = l + r >> 1; build(id << 1 , l , mid); build(id << 1 | 1 , mid + 1 , r); push_up(id , mid - tree[id].l + 1 , tree[id].r - mid); } void update(int id , int pos , int val) { if(tree[id].l == tree[id].r) { tree[id].pre = tree[id].suf = val; return ; } int mid = tree[id].l + tree[id].r >> 1; if(pos <= mid) update(id << 1 , pos , val); else update(id << 1 | 1 , pos , val); push_up(id , mid - tree[id].l + 1 , tree[id].r - mid); } ull query_range(int id , int l , int r , int ch) { if(tree[id].l >= l && tree[id].r <= r) { if(ch == 1) return tree[id].pre ; return tree[id].suf; } int mid = tree[id].l + tree[id].r >> 1; if(r <= mid) return query_range(id << 1 , l , r , ch); if(l > mid) return query_range(id << 1 | 1 , l , r , ch); ull lson = query_range(id << 1 , l , r , ch); ull rson = query_range(id << 1 | 1 , l , r , ch); ull ans ; if(ch == 1) ans = lson * power[min(r , tree[id].r) - mid] + rson; else ans = rson * power[mid - max(tree[id].l , l) + 1] + lson; return ans % mod; } void init() { power[0] = 1; rep(i , 1 , 100000) power[i] = power[i - 1] * P % mod; } signed main() { init(); int t; cin >> t; while(t --) { int n , flag = 0 , x; cin >> n; build(1 , 1 , n); rep(i , 1 , n) { cin >> x; update(1 , x , 1); if(i == 1 || i == n) continue ; int len = min(x , n - x + 1); int y = query_range(1 , x - len + 1 , x , 1); int z = query_range(1 , x , x + len - 1 , 2); if(y != z) flag = 1 ; } if(flag) cout << "Y\n"; else cout << "N\n"; } return 0; }