F-瓜瓜的跳棋
在一段区间内进行查询查询从
x
x
x点往后跳多少次不会出去区间外。
倍增
v
e
(
i
,
j
)
ve(i,j)
ve(i,j)表示从
i
i
i往后跳
2
j
−
1
2^j-1
2j−1的位置
l
(
i
,
j
)
l(i,j)
l(i,j)表示
[
i
,
i
+
2
j
−
1
]
[i,i+2^j-1]
[i,i+2j−1]可以跳的最左端位置
同理
r
r
r
二分查询即可最多可以跳多少步
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
using vii = vector<vi>;
int __INPUT__ = []() {
ios::sync_with_stdio(false);
return 0;
}();
int main()
{
int n, m;
cin >> n >> m;
vector<int>a(n);
for(auto &t: a) {
cin >> t;
t--;
}
vii ne(n,vi(20)), l(n, vi(20,INT_MAX)), r(n, vi(20, -1));
for(int i = 0; i < n; i++) {
ne[i][0] = l[i][0] = r[i][0] = a[i];
}
for(int i = 1; i < 20; i++) {
for(int j = 0; j < n; j++) {
ne[j][i] = ne[ne[j][i - 1]][i - 1];
l[j][i] = min(l[j][i - 1], l[ne[j][i - 1]][i - 1]);
r[j][i] = max(r[j][i - 1], r[ne[j][i - 1]][i - 1]);
}
}
while(m --) {
int ll, rr, x;
cin >> ll >> rr >> x;
ll--, rr--, x--;
int L = 0, R = n + 1, res = 0;
auto check = [ll, rr, &l, &r, &ne](int now, int len)->bool{
int mi = now, ma = now;
for(int i = 19; i >= 0; i--) {
if(len >> i & 1) {
mi = min(mi, l[now][i]);
ma = max(ma, r[now][i]);
now = ne[now][i];
}
}
return mi >= ll && ma <= rr;
};
while(L <= R) {
int mid = L + R >> 1;
if(check(x, mid)) {
res = mid;
L = mid + 1;
} else {
R = mid - 1;
}
}
if(res == n + 1) {
cout << "INF\n";
} else {
cout << res << '\n';
}
}
}