现在起点不唯一了,一种解决的方法是:对所有询问离线处理(按照右端点排序),每次考虑到一个新的数 a_r 时,用某个数据结构同时维护 [f_1,f_2,\cdots,f_n],其中 f_i 表示以 i 为起点时能删掉的数的个数。
我们需要用数据结构找到这个位置 $k$ ,然后把区间 $[1,k]$ 都加 $1$ 。
可以用树状数组来实现,$[1,k]$ 区间加 $1$ ,对应差分数组是 $d_1$ 加 $1$ ,$d_{k+1}$ 减 $1$ 。每次都是 $d_1$ 加 $1$ ,那干脆只维护每个位置减 $1$ 的个数好了。那么怎么找 $k+1$ 呢?当前总共加的次数是 $r-1$ ,那么减的总数最多到 $r-1-a_r$ 还是可以满足 $f_i \ge a_r$ 的,也就是说这个分界点 $k+1$ 在第 $r-1-a_r+1$ 个减一操作的位置。那么用树状数组求第 $K$ 小来找这个位置即可。
参考代码如下:
```c++
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const int N = 300005;
int a[N];
int c[N];
int sum(int x) {
int res = 0;
while (x) {
res += c[x];
x -= x & -x;
}
return res;
}
void add(int x, int d, int n) {
while (x <= n) {
c[x] += d;
x += x & -x;
}
}
int kth(int k, int n) {
int x = 0;
for (int i = 1 << 18; i; i >>= 1) {
if (x + i <= n && k - c[x + i] > 0) {
x += i;
k -= c[x];
}
}
return x + 1;
}
struct Node {
int x, y, id;
} b[N];
bool cmp(Node p1, Node p2) {
return p1.y < p2.y;
}
int ans[N];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
a[i] = i - a[i];
}
for (int i = 0; i < m; i++) {
scanf("%d%d", &b[i].x, &b[i].y);
b[i].x = 1 + b[i].x;
b[i].y = n - b[i].y;
b[i].id = i;
}
sort(b, b + m, cmp);
int pos = 1;
for (int i = 0; i < m; i++) {
while (pos <= b[i].y) {
int k = a[pos] < 0 ? 1 : min(kth(pos - a[pos], n), pos + 1);
add(k, 1, n);
pos++;
}
ans[b[i].id] = b[i].y - sum(b[i].x);
}
for (int i = 0; i < m; i++) {
printf("%d\n", ans[i]);
}
return 0;
}
```