[BZOJ]2124 等差子序列 Hash&树状数组

来源:互联网 发布:阿里云服务器停止不了 编辑:程序博客网 时间:2024/06/06 06:21

2124: 等差子序列

Time Limit: 3 Sec  Memory Limit: 259 MB
Submit: 1719  Solved: 648
[Submit][Status][Discuss]

Description

给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pLen<=N (Len>=3),
使得Ap1,Ap2,Ap3,…ApLen是一个等差序列。

Input

输入的第一行包含一个整数T,表示组数。
下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开。
N<=10000,T<=7

Output

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

Sample Input

2
3
1 3 2
3
3 2 1

Sample Output

N
Y

HINT

Source

[Submit][Status][Discuss]


HOME Back

    好神啊好神啊...  以前认为Hash啥的不会很难, 果然还是我太弱了, 感觉还远远不够啊... orz了题解.

    我们考虑题目中所描述的三个数的中间的那个数, 与之构成等差子序列的数为p和q, 位置p < x < q.  如果是从左到右枚举的话, 那么枚举到x, 如果有这样的p和q, 那么就应该是p出现过, q还没出现(x < q). 那么我们又知道q - x = x - p. 那么也就是说枚举到x的话, 只要前面存在与x相差d的一个p, 且当前x + d还没有出现的话,就能说明位置上p < x < x + d(注意这是个排列 ),q就是x+d,  就能满足条件, 可以输出"Y"了。 

    我们会发现由等差这个性质可以得到p 是和x+d在值域上关于x对称(x - p = x + d - x). 那么用0, 1在值域上表示出或者没出现过, 当前枚举到x, 值域上关于x只要有一个不对称就能满足条件(因为这就说明了一个出现过一个没出现过,值域位置对称保证了等差, 一个出现一个没出现保证了序列上的位置关系). 然而我们又能发现不能满足条件的情况就是没有一个不对称 -- 那不就是处处都对称?? 这不就是值域上以x为中心的回文串吗? 那我们只需要判断以x为中心的极长字符串是不是回文串就可以了(因为这中间肯定有某个位置不对称才会导致这个字符串不是回文串). 判断用manacher? 实际上分成两部分hash判相等就可以了, 修改和查询可以用树状数组完成, 由于是分成两部分正反判相等, 要用到两个hash值. 详见代码.

    启示: 就是当题目中给的限制屈指可数的时候, 可以尝试着枚举来找相邻之间的关系, 从限制的多个角度来考虑求解(位置, 值域...), 过程中还能从命题和反命题两方面思考来进行简化.

    之前都不明白树状数组的性质乱用, 果然这道题就不知道怎么提取区间hash值了. 问了Doggu才知道hh.(加了读优还是比Doggu慢6ms咩?

#include<bits/stdc++.h>using namespace std;const int maxn = 1e4 + 5;int T, n, a[maxn];unsigned int pw[maxn];struct Binary {int i;unsigned int c[maxn], tmp1, tmp2;inline void clear() {memset(c, 0, sizeof(c));}inline void modify(const int &x) {for (i = x; i <= n; i += i & -i) c[i] += pw[i - x];}inline unsigned int query(const int &p, const int &q) {tmp1 = tmp2 = 0;for (i = p - 1; i; i ^= i & -i) tmp1 += c[i] * pw[p - 1 - i];for (i = q; i; i ^= i & -i) tmp2 += c[i] * pw[q - i];return tmp2 - tmp1 * pw[q - p + 1];}}bit1, bit2;int main() {scanf("%d", &T);register int i, x;for (pw[0] = 1, i = 1; i < maxn; ++ i) pw[i] = pw[i - 1] * 10007;while (T --) {scanf("%d", &n);for (i = 1; i <= n; ++ i) scanf("%d", &a[i]);for (i = 1; i <  n; ++ i) {x = min(a[i] - 1, n - a[i]);if (x && bit1.query(a[i] - x, a[i] - 1) != bit2.query(n - a[i] - x + 1, n - a[i])) break;bit1.modify(a[i]), bit2.modify(n - a[i] + 1);}(i >= n) ? puts("N") : puts("Y");if (T) bit1.clear(), bit2.clear(); }return 0;}


阅读全文
0 0
原创粉丝点击