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;
}
posted @ 2020-06-01 17:45  GsjzTle  阅读(245)  评论(0编辑  收藏  举报