[CF1404C] Fixed Point Removal
[题目链接]
https://codeforces.com/contest/1404/problem/C
[题解]
忽略多组询问 , 首先考虑如果只有一组询问怎么做。
对于每个数 , 若 \(a_{i} > i\) , 那么该数无论怎样调整都不会改变被移除。 否则该数在前面删去 \((i - a_{i})\) 个数后也会变得可以删去。 不妨定义新权值 \(a'_{i} = i - a_{i}\)。
率先删去靠后的数必然不劣 , 因为不会影响到前面的数。
那么用数据结构维护 \(a'\) 序列 , 需要支持查询区间减和最小值 , 最小值的个数。 可以用线段树实现。
下面考虑有询问的情况 : 将询问离线 , 按右端点从大到小排序 , 按照上面的做法 , 在删去一个数的时候 , 在另一个数据结构 (如树状数组) 上的对应位置 \(+1\) , 询问的就是某个前缀的贡献和。
时间复杂度 : \(O((N + Q)logN)\)
[代码]
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MN = 3e5 + 5;
const int INF = 2e9;
struct node {
int l , r , home;
} q[MN];
int N , Q;
int mn[MN << 2] , tag[MN << 2] , c[MN] , ans[MN] , val[MN] , a[MN];
inline bool cmp(node a , node b) {
return a.l > b.l;
}
inline void pushup(int now) {
mn[now] = min(mn[now << 1] , mn[now << 1 | 1]);
}
inline void pushdown(int now) {
if (tag[now] != 0) {
mn[now << 1] += tag[now]; tag[now << 1] += tag[now];
mn[now << 1 | 1] += tag[now]; tag[now << 1 | 1] += tag[now];
tag[now] = 0;
}
}
inline void dec(int now , int l , int r , int ql , int qr) {
if (ql > qr) return;
if (l == ql && r == qr) {
--mn[now]; --tag[now];
return;
}
int mid = l + r >> 1; pushdown(now);
if (mid >= qr) dec(now << 1 , l , mid , ql , qr);
else if (mid + 1 <= ql) dec(now << 1 | 1 , mid + 1 , r , ql , qr);
else dec(now << 1 , l , mid , ql , mid) , dec(now << 1 | 1 , mid + 1 , r , mid + 1 , qr);
pushup(now);
}
inline int query(int now , int l , int r , int x) {
if (l == r) return mn[now] > 0 ? INF : l;
int mid = l + r >> 1; pushdown(now);
if (mid < x) return query(now << 1 | 1 , mid + 1 , r , x);
else if (mn[now << 1 | 1] == 0) return query(now << 1 | 1 , mid + 1 , r , x);
else return query(now << 1 , l , mid , x);
}
inline void change(int now , int l , int r , int x , int val) {
if (l == r) {
mn[now] = val;
return;
}
int mid = l + r >> 1; pushdown(now);
if (mid >= x) change(now << 1 , l , mid , x , val);
else change(now << 1 | 1 , mid + 1 , r , x , val);
pushup(now);
}
inline void add(int x) {
for (int i = x; i <= N; i += i & -i) ++c[i];
}
inline int qry(int x) {
int res = 0;
for (int i = x; i; i -= i & -i) res += c[i];
return res;
}
int main() {
scanf("%d%d" , &N , &Q);
for (int i = 1; i <= 4 * N; ++i) mn[i] = INF;
for (int i = 1; i <= N; ++i) scanf("%d" , &a[i]);
for (int i = 1; i <= Q; ++i) scanf("%d%d" , &q[i].l , &q[i].r) , q[i].home = i;
sort(q + 1 , q + 1 + Q , cmp);
int cur = 1;
for (int i = N; i; --i) {
change(1 , 1 , N , i , a[i] > i ? 1e9 : i - a[i]);
while (mn[1] == 0) {
int x = query(1 , 1 , N , i); if (x > N) break;
add(x) , change(1 , 1 , N , x , INF) , dec(1 , 1 , N , x + 1 , N);
}
while (cur <= Q && q[cur].l == i - 1) {
ans[q[cur].home] = qry(N - q[cur].r);
++cur;
}
}
for (int i = 1; i <= Q; ++i) printf("%d\n" , ans[i]);
return 0;
}