您的位置:首页 > 其它

「6月雅礼集训 2017 Day11」jump

2017-06-27 14:08 351 查看
【题目大意】

有$n$个位置,每个位置有一个数$x_i$,代表从$i$经过1步可以到达的点在$[\max(1, i-x_i), \min(i+x_i, n)]$中。

定义$(i,j)$的距离表示从$i$到$j$经过多少步,从$j$到$i$经过多少步,这两个取最小值。

求任意两点间最大的距离。

$1\leq n \leq 10^5, 1 \leq x_i < n$

【题解】

每个点经过若干次能过到达的,显然是一个区间。

考虑倍增,$[L_{x,i}, R_{x,i}]$表示从$x$开始,经过$2^i$步,到达的区间。

这个可以通过倍增+线段树来解决,线段树维护最小值和最大值,对应区间两个端点。

对于询问,我们二分答案后,问题转化与是否能找出一个点对步数$> x$($x$为我们二分的值)

通过倍增我们可以求出每个点经过$x$步后到达的区间,设为$[L', R']$。那么不能到达的就是$[1, L']$和$[R',n]$。

问题转化为,$[1, L']$和$[R', n]$是否可以经过$x$步以内到达$i$这个点。

这个可以通过记录一个前缀和以及一个后缀和来解决。

这里的“和”指的是区间交。

然后就行啦!

时间复杂度$O(nlog^3n)$

upd: 线段树有点问题,已经修正

=========================分割线=============================

upd: 之前的复杂度有点问题,是$O(nlog^3n)$的,理论上是过不去的,但是常数太优秀了(逃

我们可以把二分改为从高位往低位确定答案的每位,每次就不需要倍增了,就在之前的基础上,直接做即可。这样就是$O(nlog^2n)$的了

# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;

const int N = 1e5 + 10, M = 2e5 + 10;
const int inf = 1e9;

# define bit(x, i) (((x) >> (i)) & 1)

int n;
struct pa {
int x, y;
pa() {}
pa(int x, int y) : x(x), y(y) {}
friend pa operator + (pa a, pa b) {
return pa(min(a.x, b.x), max(a.y, b.y));
}
friend pa operator - (pa a, pa b) {
return pa(max(a.x, b.x), min(a.y, b.y));
}
};

inline bool out(int x, pa t) {
return x < t.x || x > t.y;
}

int L[18]
, R[18]
;

struct SMT {
pa w[M << 1];
# define ls (x<<1)
# define rs (x<<1|1)
inline void set(int n) {
for (int i=0; i<=n+n; ++i) w[i] = pa(inf, -inf);
}
inline void build(int x, int l, int r, int p) {
if(l == r) {
w[x] = pa(L[p][l], R[p][l]);
return ;
}
int mid = l+r>>1;
build(ls, l, mid, p);
build(rs, mid+1, r, p);
w[x] = w[ls] + w[rs];
//        printf("p = %d, [%d, %d],  [%d, %d]\n", p, l, r, w[x].x, w[x].y);
}
inline pa query(int x, int l, int r, int L, int R) {
if(L <= l && r <= R) return w[x];
int mid = l+r>>1;
if(L > mid) return query(rs, mid+1, r, L, R);
else if (R <= mid) return query(ls, l, mid, L, R);
else return query(ls, l, mid, L, mid) + query(rs, mid+1, r, mid+1, R);
}
# undef ls
# undef rs
}T[18];

// have dis > x
pa c
;

pa f
, g
;

inline bool chk(int x) {
//    cout << "x = " << x << endl;
pa t;
for (int i=1; i<=n; ++i) {
t.x = t.y = i;
for (int j=17; ~j; --j)
if(bit(x, j)) t = T[j].query(1, 1, n, t.x, t.y);
c[i] = t;
//        printf("  i = %d, [%d, %d]\n", i, c[i].x, c[i].y);
}
f[1] = c[1]; g
= c
;
for (int i=2; i<=n; ++i) f[i] = f[i-1] - c[i];
for (int i=n-1; i; --i) g[i] = g[i+1] - c[i];
for (int i=1; i<=n; ++i) {
if(c[i].x == 1 && c[i].y == n) continue;
// [1, c[i].x-1],  [c[i].y+1], n]
if(c[i].x != 1) {
if(out(i, f[c[i].x-1])) return true;
}
if(c[i].y != n) {
if(out(i, g[c[i].y+1])) return true;
}
}
return false;
}
// # include <time.h>
int main() {
freopen("jump.in", "r", stdin);
freopen("jump.out", "w", stdout);
cin >> n;
for (int i=0; i<=17; ++i) T[i].set(n);
for (int i=1, x; i<=n; ++i) {
scanf("%d", &x);
L[0][i] = max(i-x, 1);
R[0][i] = min(i+x, n);
}
pa tem;
for (int j=1; j<=17; ++j) {
T[j-1].build(1, 1, n, j-1);
for (int i=1; i<=n; ++i) {
tem = T[j-1].query(1, 1, n, L[j-1][i], R[j-1][i]);
L[j][i] = tem.x, R[j][i] = tem.y;
}
}
T[17].build(1, 1, n, 17);

//    for (int j=0; j<=5; ++j)
//        for (int i=1; i<=n; ++i) printf("%d %d L = %d, R = %d\n", i, j, L[j][i], R[j][i]);

int l = 0, r = n-1, mid, ans;
while(1) {
if(r-l <= 3) {
for (int i=r; i>=l; --i)
if(chk(i)) {
ans = i;
break;
}
break;
}
mid = l+r>>1;
if(chk(mid)) l = mid;
else r = mid;
}

cout << ans + 1 << endl;
//    cerr << clock() << " ms\n";
return 0;
}
/*
8
7 1 1 1 1 1 1 7

10
2 2 1 2 2 1 2 2 1 2
*/


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: