题解
为了方便计算, 我们将原数组, 全部替换为 a[i] = i - a[i]
对于 a[i] = 0 的是直接可以删除的, a[i] < 0 是不能删除的, 可以变为 a[i] = n + 1(不可能删除)
每个询问 x, y 针对的区间是 [x + 1, n - y], l = x + 1, r = n - y
因为是,从左向右, 我们考虑枚举 r
假设我们知道了 F[1, r - 1] 的答案, 那么 F[1, r] = F[1, r - 1] + (a[i] <= F[1, r - 1])
假设我们知道了 F[l, r - 1] 的答案, 那么 F[l, r] = F[l, r - 1] + (a[i] <= F[l, r - 1])
问题就是如何求 F[l, l] 即 a[l] 对 [1~l, l~n] 的贡献
由于是枚举r, 可以简化 F[l, r] 为 当前 r 下 F[l]
对于 1 < l < r, 我们在 r = r - 1的时候 F[l] 已经知道了
我们只要计算 F[r] 对 F[1 ~ r - 1] 的影响就行了
即 找到最大的 l_max, 使得 F[l_max] == a[r], 那么 F[1 ~ l_max] ++, 所有都使得 a[i] 变成0 (在 r 固定的前提下)
ps: 为什么找最大? 是l_max, 使得 F[1 ~ l_max - 1]++ 的, 本质是 l_max 做出最后的贡献, 使得 a[i] 变成 0
这样 原本 for (r, 1, n) for (l, 1, r) 的 O(n^2) 就变成了
for (r, 1, n) find(l_max) 想办法优化 find(l_max) 这一步
在找到 l_max 设计了区间操作, 那么就有了线段树, 树状数组等数据结构, 增删改查上限 都是 O(logn) 复杂度就够了
找 l_max 用二分
最后按照 r = n - y 的顺序 离线 回答问题即可
#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef vector<int> VI;
const int N = 3e5 + 5;
int n, m, _, k;
int c[N], a[N], ans[N];
vector<PII> q[N];
void add(int x, int k) {
for (; x <= n; x += -x & x) c[x] += k;
}
int ask(int x) {
int ans = 0;
for (; x; x -= x & -x) ans += c[x];
return ans;
}
int main() {
IOS; cin >> n >> m;
rep (i, 1, n) cin >> k, a[i] = i - k < 0 ? n + 1 : i - k;
rep (i, 1, m) {
int x, y; cin >> x >> y;
q[n - y].pb({ x + 1, i });
}
rep (i, 1, n) {
int l = 0, r = i;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (ask(mid) >= a[i]) l = mid;
else r = mid - 1;
}
add(1, 1); add(l + 1, -1);
for (auto &[x, idx] : q[i]) ans[idx] = ask(x);
}
rep (i, 1, m) cout << ans[i] << '
';
return 0;
}