[CF1404C] Fixed Point Removal - 离线处理,线段树上二分

Description

给定一个含有 \(n\) 个正整数的序列 \(a\) ,对于一次操作,你可以任选一个位置 \(i\) 且满足 \(a_i=i\) ,那么就可以移除这个元素,并将后面所有的元素向前移动一位。对于每个相互独立的询问 \(x,y\) 需要你求出在前 \(x\) 个元素以及后 \(y\) 个元素不能被移除的情况下,最多可以进行几次操作。\(n,q \leq 3\times 10^5\)

Solution

对每个位置的元素,线段树维护它在整个过程中的 \(i-a_i\),一个元素显然只有在 \(i-a_i \ge 0\) 的情况下才是有救的

对询问离线,考虑将所有元素从右向左一个个加入的过程,用一个 BIT 维护每个元素是否能被删除,这样待会询问的时候左端点时隐含的,我们在树状数组上查询右端点左边的部分的和就是可以删除的次数

现在我们要动态维护这个删除过程,注意我们不会直接删除元素,只会修改它在线段树上的 \(i-a_i\),比如减 \(1\) 就代表这个元素往左移动了,设为 \(\infty\) 就代表这个元素被删除了

我们每次挑出最靠右的满足 \(i-a_i=0\) 的元素,把它在线段树上的值设为 \(\infty\),右边的全部 \(-1\),然后在树状数组上修改一下

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 2e6 + 5;

int tag[N], val[N];

void pushup(int p)
{
    val[p] = min(val[p * 2], val[p * 2 + 1]);
}

void put(int p, int v)
{
    val[p] += v;
    tag[p] += v;
}

void pushdown(int p, int l = 0, int r = 0)
{
    if (tag[p] == 0)
        return;
    put(p * 2, tag[p]);
    put(p * 2 + 1, tag[p]);
    tag[p] = 0;
}

void SegModify(int p, int l, int r, int ql, int qr, int key)
{
    if (l > qr || r < ql)
        return;
    if (l >= ql && r <= qr)
        put(p, key);
    else
    {
        pushdown(p, l, r);
        SegModify(p * 2, l, (l + r) / 2, ql, qr, key);
        SegModify(p * 2 + 1, (l + r) / 2 + 1, r, ql, qr, key);
        pushup(p);
    }
}
void SegModify(int p, int l, int r, int pos, int key)
{
    if (l == r)
    {
        val[p] = key;
    }
    else
    {
        pushdown(p);
        if (pos <= (l + r) / 2)
            SegModify(p * 2, l, (l + r) / 2, pos, key);
        else
            SegModify(p * 2 + 1, (l + r) / 2 + 1, r, pos, key);
        pushup(p);
    }
}
int SegQuery(int p)
{
    return val[p];
}

int SegBisect(int p, int l, int r)
{
    if (l == r)
        return l;
    pushdown(p, l, r);
    if (val[p * 2 + 1] == 0)
        return SegBisect(p * 2 + 1, (l + r) / 2 + 1, r);
    else
        return SegBisect(p * 2, l, (l + r) / 2);
}

int arr[N];

#define lowbit(x) (x & (-(x)))

void BitAdd(int p)
{
    if (p == 0)
        return;
    while (p < N)
    {
        arr[p]++;
        p += lowbit(p);
    }
}

int BitQuery(int p)
{
    int ans = 0;
    while (p > 0)
    {
        ans += arr[p];
        p -= lowbit(p);
    }
    return ans;
}

int n, q;

struct Query
{
    int l, r, id;
    bool operator<(const Query &rhs) const
    {
        return l > rhs.l;
    }
} que[N];

int a[N], ans[N];

signed main()
{
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= q; i++)
        cin >> que[i].l >> que[i].r, que[i].id = i;
    sort(que + 1, que + q + 1);

    int qid = 1;

    memset(val, 0x3f, sizeof val);

    for (int i = n; i >= 1; i--)
    {
        SegModify(1, 1, n, i, a[i] <= i ? i - a[i] : 1e9);
        while (SegQuery(1) == 0)
        {
            int pos = SegBisect(1, 1, n);
            BitAdd(pos);
            SegModify(1, 1, n, pos, 1e9);
            SegModify(1, 1, n, pos + 1, n, -1);
        }
        while (qid <= q && que[qid].l == i - 1)
        {
            // cout << "...";
            ans[que[qid].id] = BitQuery(n - que[qid].r);
            ++qid;
        }
    }

    for (int i = 1; i <= q; i++)
        cout << ans[i] << endl;
}

相关文章: