CF1405E. Fixed Point Removal
\(\texttt{Difficulty:2300}\)
题意
一个长为 \(n(1\le n\le3\cdot10^5)\) 的序列 \(a(1\le a_i\le n)\) ,每次操作可以选择一个 \(a_i=i\) 的数删去,后面的元素整体前移一个位置。 \(q(1\le q\le3\cdot10^5)\) 次询问,每次询问 \(l,r\) ,求在区间 \([1,l]\) 和 \([r,n]\) 被禁止删除的情况下,最多能删除多少次。
思路
考虑令 \(a_i=i-a_i\) ,于是可以发现当前可以删除的数字就是 \(a_i=0\) 的位置,并且删除后其后面所有位置的值 \(-1\) ,显然 \(a_i\) 为负数则永远不可能被删除。考虑将询问离线,对询问按右端点排序,之后维护序列 \(f\) , \(f_i\) 表示从 \(i\) 开始到当前所能删除的总个数,由于一个数字想要被删除,其前面能被删除的数的个数至少为 \(a_i\) ,满足这个条件的话我们每次删掉最右边的 \(a_i=0\) 的数一定可以删完所有这样的数。又因为 \(f\) 显然是单调递减的,所以当前位置只会对 \(f\) 的一个前缀产生 \(1\) 的贡献。为了方便更新贡献,我们改为维护 \(f\) 的差分数组 \(d\) ,这样对于前缀 \([1,k]\) ,只需要 \(d_1+1,d_{k+1}-1\) 即可,又因为每次都在 \(d_1\) 处 \(+1\) ,所以只需维护 \(-1\) 的位置即可。这部分可以用树状数组来维护,之后我们需要快速找出 \(k+1\) 的位置,只需找到最后一个 \(f_i>=a_i\) 的 \(i\) 即可,这部分可以用树状数组上二分来解决,每个询问的右端点处的 \(f_l\) 即为答案,复杂度 \(O(nlogn)\) 。
代码
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<LL, LL>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc tr[x].ch[0]
#define rc tr[x].ch[1]
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 300010;
int N, Q, A[maxn], ans[maxn], d[maxn];
struct Query {
int l, r, id;
}q[maxn];
void add(int i, int x)
{
while (i <= N)
d[i] += x, i += i & (-i);
}
int sum(int i)
{
int ret = 0;
while (i)
ret += d[i], i -= i & (-i);
return ret;
}
int kth(int k)
{
int x = 0;
for (int i = 19; i >= 0; i--)
{
int t = 1 << i;
if (x + t <= N && k >= d[x + t])
k -= d[x + t], x += t;
}
return x + 1;
}
bool cmp(const Query& a, const Query& b)
{
return a.r < b.r;
}
void solve()
{
int now = 0;
sort(q + 1, q + Q + 1, cmp);
for (int i = 1; i <= Q; i++)
{
while (now < q[i].r)
{
int k;
if (A[now + 1] >= 0)
k = kth(now - A[now + 1]);
else
k = 1;
k = min(now + 2, k);
add(k, 1), now++;
}
ans[q[i].id] = q[i].r - sum(q[i].l);
}
for (int i = 1; i <= Q; i++)
cout << ans[i] << endl;
}
int main()
{
IOS;
cin >> N >> Q;
for (int i = 1; i <= N; i++)
cin >> A[i], A[i] = i - A[i];
for (int i = 1; i <= Q; i++)
cin >> q[i].l >> q[i].r, q[i].id = i, q[i].r = N - q[i].r, q[i].l++;
solve();
return 0;
}

浙公网安备 33010602011771号