赛时思路:
枚举 j ,则只有 [j−aj+1,j]∪(j,j+aj−1] 有可能到 j 。用树状数组判掉。
考虑 i 在所有点都能到的情况下不合法的情况,当且仅当存在两个位置 l≤i≤r ,使得 al≤r−l∧ar≤r−l 。
变形,得到 l+al≤r∧r−ar≥l 。显然要找最大的 r−ar 。
若 l+al≥i ,那么应该在这个位置右侧判断,否则在 i 右侧判断。前一种情况可以覆盖一个区间的 i([l,l+al]),后一种情况只需要记录最小的 l 。
代码:
#include <bits/stdc++.h>
using namespace std;
#define CF
#define ll long long
#define ul unsigned ll
#define LL __int128_t
#define db double
#define DB long db
#define pii pair<int, int>
#define fi first
#define se second
#define mkpr make_pair
#define mem(a, x) memset(a, x, sizeof(a))
#define rep(i, l, r) for (int i(l), i##End(r); i <= i##End; i = -~i)
#define per(i, r, l) for (int i(r), i##End(l); i >= i##End; i = ~-i)
template<typename T, typename U> void chkmx(T &_a, U _b) { if (_a < _b) _a = _b; }
template<typename T, typename U> void chkmn(T &_a, U _b) { if (_a > _b) _a = _b; }
constexpr int N = 200005;
constexpr ll mod = 998244353;
int n, p, ans;
int a[N];
set<pii> S;
int mx[N];
struct BITree
{
int s[N];
void modify(int p, int k) { for (; p <= n; p += p & -p) s[p] += k; }
int query(int p) { int res = 0; for (; p; p -= p & -p) res += s[p]; return res; }
} T;
void solve()
{
S.clear(); rep(i, 1, n) T.s[i] = 0;
scanf("%d", &n);
rep(i, 1, n) scanf("%d", &a[i]);
rep(j, 1, n)
{
if (j - a[j] + 1 <= j) T.modify(max(j - a[j] + 1, 1), 1), T.modify(j + 1, -1);
if (j < a[j] + j - 1) T.modify(j + 1, 1), T.modify(min(a[j] + j, n + 1), -1);
}
mx[n] = n - a[n]; per(i, n - 1, 1) mx[i] = max(mx[i + 1], i - a[i]);
p = n + 1, ans = 0; rep(i, 1, n)
{
if (T.query(i) == n && mx[i] < p) ans++;
if (mx[i + a[i]] >= i) T.modify(i, -1), T.modify(i + a[i] + 1, 1);
S.emplace(i + a[i], i);
while (!S.empty() && S.begin()->fi < i) chkmn(p, S.begin()->se), S.erase(S.begin());
// #ifndef ONLINE_JUDGE
// printf("%d: %d %d %d\n", i, p, T.query(i), mx[i]);
// for (auto [x, y] : S) printf(" - [%d %d]\n", x, y);
// #endif
}
printf("%d\n", ans);
}
void init(){}
int main()
{
init();
#ifdef CF
int _; scanf("%d", &_);
while (_--) solve();
#else
solve();
#endif
return 0;
}