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

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值