【权值树状数组\思维题】P2757 [国家集训队]等差子序列

https://www.luogu.com.cn/problem/P2757

题目描述

给一个1到N的排列{Ai},询问是否存在

1≤p1<p2<p3<p4<p5<…<pLen≤N(Len≥3)

使得Ap1,Ap2,Ap3,⋯ ,ApLen是一个等差序列。

输入格式

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

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

输出格式

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

输入输出样例

输入 #1
2
3
1 3 2
3
3 2 1
输出 #1
N
Y

说明/提示

对于5%的数据,N<=100

对于30%的数据,N<=1000

对于100%的数据,N<=10000,T<=7

我们可以想到一个很简单的T*n^2的暴力,len显然3,然后i<j<k,aj*2==ai+ak。

之后我们再想,枚举的aj,它可能的ai和aj的枚举取值范围?

若aj*2-1<=n,则取值[1,aj*2-1]

若aj*2-1>n,则取值[aj*2-n,n]

那么可以发现,对一个aj无法找到左一个右一个加起来==2*aj,满足它们成对出现在左边或右边,那么就有可能取值范围内的左边数之和为2*aj的倍数,右边同理。而如果存在左一个右一个那么左右两边处在取值范围内的数的和分别都不应是2*aj的倍数。

由于具有对称性,所以我们只需要判断左边就足够了,满足左边在取值范围内的数的和不为2*aj的倍数yes,否则no.

#include <bits/stdc++.h>
#define lowbit(x) ((x) & (-x))
typedef long long ll;
const int maxn = 1e4 + 5;
ll c[maxn], n;
void ad(int x, int d) {
    for (; x <= n; x += lowbit(x))
        c[x] += d;
}
ll gsm(int x) {
    ll sm = 0;
    for (; x; x -= lowbit(x))
        sm += c[x];
    return sm;
}
using namespace std;
ll a[maxn], lm[maxn];
void sol() {
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &a[i]);
    for (int i = 1; i <= n; i++)
        c[i] = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i] * 2 - 1 <= n)
            lm[i] = gsm(a[i] * 2 - 1);
        else
            lm[i] = gsm(n) - gsm(a[i] * 2 - n - 1);
        ad(a[i], a[i]);
    }
    for (int i = 1; i <= n; i++) {
        if (lm[i] && (lm[i] % (2 * a[i]))) {
            puts("Y");
            return;
        }
    }
    puts("N");
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--)
        sol();
    return 0;
}
posted @ 2021-11-14 22:20  Newuser233  阅读(38)  评论(0编辑  收藏  举报