您的位置:首页 > 其它

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

2017-12-12 20:10 253 查看

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: