【题目大意】
有$n$个位置,每个位置有一个数$x_i$,代表从$i$经过1步可以到达的点在$[max(1, i-x_i), min(i+x_i, n)]$中。
定义$(i,j)$的距离表示从$i$到$j$经过多少步,从$j$到$i$经过多少步,这两个取最小值。
求任意两点间最大的距离。
$1leq 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][N], R[18][N]; 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] ", 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[N]; pa t[N]; pa f[N], g[N]; inline bool chk(int x) { pa tmp; for (int i=1; i<=n; ++i) { tmp.x = t[i].x, tmp.y = t[i].y; tmp = T[x].query(1, 1, n, tmp.x, tmp.y); c[i] = tmp; // printf(" i = %d, [%d, %d] ", i, c[i].x, c[i].y); } f[1] = c[1]; g[n] = c[n]; 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 ", i, j, L[j][i], R[j][i]); int l = 0, r = n-1, mid, ans = 0; for (int i=1; i<=n; ++i) t[i] = pa(i, i); for (int i=16; ~i; --i) { if(chk(i)) { for (int j=1; j<=n; ++j) t[j] = c[j]; ans += (1<<i); } } cout << ans + 1 << endl; // cerr << clock() << " ms "; return 0; } /* 8 7 1 1 1 1 1 1 7 10 2 2 1 2 2 1 2 2 1 2 */
下面是$O(nlog^3n)$的代码
# 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][N], R[18][N]; 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] ", 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[N]; pa f[N], g[N]; 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] ", i, c[i].x, c[i].y); } f[1] = c[1]; g[n] = c[n]; 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 ", 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 "; return 0; } /* 8 7 1 1 1 1 1 1 7 10 2 2 1 2 2 1 2 2 1 2 */