您的位置:首页 > 其它

CF 452F Permution 神奇的线段树判断

2016-08-18 14:50 411 查看

题目大意

给定一个1到N的列,问是否存在一个长度为3的子等差数列。

N≤300000

解题思路

看到这题,感觉有点无从下手,好像怎么要都要枚举两个值。

但是我们可以发现一个很神奇的性质如果对于一个位置i,它是子等差序列中的第二项,那么如果不存在长度为3的子等差数列,那么Ai+k和Ai−k在位置i前肯定是成对存在的,即如果我们把位置i前出现的数都在数组上打上tag,那么这些tag肯定是关于Ai对称的(在边界之内)!那么我们可以用Hash来维护数组中连续一段数的存在情况,但是对于数组中任意一个位置都要判断是否合法。那么只需在线段树上打tag,然后线段树中每个结点存的是Hash值,那么从前往后,边维护边判断即可。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
typedef long long LL;

const int Mo = 1e9 + 7;
const int MAXN = 3e5 + 5;

struct Node {
LL Num;
int len;
Node (LL num, int Len) {Num = num, len = Len;}
Node (){}
} Tr[2][MAXN * 4];

int N;
LL Pow[MAXN];

Node Merge(Node L, Node R) {
Node New;
New.len = L.len + R.len;
New.Num = (1ll * L.Num * Pow[R.len] + R.Num) % Mo;
return New;
}

void Build(int Ord, int Now, int l, int r) {
Tr[Ord][Now].len = r - l + 1;
if (l == r) return;
int Mid = (l + r) >> 1;
Build(Ord, Now * 2, l, Mid), Build(Ord, Now * 2 + 1, Mid + 1, r);
}

void Modify(int Ord, int Now, int l, int r, int Side) {
if (l == r) {
Tr[Ord][Now].Num = 1;
return;
}
int Mid = (l + r) >> 1;
if (Side <= Mid) Modify(Ord, Now * 2, l, Mid, Side); else
Modify(Ord, Now * 2 + 1, Mid + 1, r, Side);
if (!Ord) Tr[Ord][Now] = Merge(Tr[Ord][Now * 2], Tr[Ord][Now * 2 + 1]); else
Tr[Ord][Now] = Merge(Tr[Ord][Now * 2 + 1], Tr[Ord][Now * 2]);
}

Node Query(int Ord, int Now, int l, int r, int lx, int rx) {
if (lx > rx) return Node(0, 0);
if (l == lx && r == rx) return Tr[Ord][Now];
int Mid = (l + r) >> 1;
if (rx <= Mid) return Query(Ord, Now * 2, l, Mid, lx, rx); else
if (lx > Mid) return Query(Ord, Now * 2 + 1, Mid + 1, r, lx, rx); else {
Node L = Query(Ord, Now * 2, l, Mid, lx, Mid);
Node R = Query(Ord, Now * 2 + 1, Mid + 1, r, Mid + 1, rx);
if (!Ord) return Merge(L, R); else
return Merge(R, L);
}
}

int main() {
freopen("data.in", "r", stdin), freopen("data.out", "w", stdout);

scanf("%d", &N);
Pow[0] = 1;
Build(0, 1, 1, N), Build(1, 1, 1, N);
for (int i = 1; i <= N; i ++) Pow[i] = 1ll * Pow[i - 1] * 2 % Mo;
for (int i = 1; i <= N; i ++) {
int Now;
scanf("%d", &Now);
int len = min(Now - 1, N - Now);
if (Query(0, 1, 1, N, Now - len, Now - 1).Num != Query(1, 1, 1, N, Now + 1, Now + len).Num) {
printf("YES\n");
return 0;
}
Modify(0, 1, 1, N, Now);
Modify(1, 1, 1, N, Now);
}
printf("NO\n");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: