线段树/树状数组(codeforces 1404C Fixed Point Removal)

题目链接
题意:给你一个长度为n的数组。
我们定义一种操作,如果数组中有某个数满足 a i = i a_i=i ai=i,即值等于它的下标,你就可以删去它,它后面的元素将全部往前移动一格。
现在有m次查询,每次查询给你一对x,y。你要回答出,在不删除前x个和后y个元素的情况下,你最多可以进行多少次上述操作。
题解:
挺有意思的一道线段树。我们考虑离线,先把查询按照x排序。
我们先考虑:对于一个元素而言,如果它的值大于了它的下标,那么这个元素就肯定不可能被删除。那么我们现在设: g = i − a i g=i-a_i g=iai。即如果想要删除 a i a_i ai。我们需要把它往前移多少位,那么这时,显然,如果在第i个元素之前可以删除的元素个数大于等于g,那么 a i a_i ai也是一个可以删除的元素。(我们只要将前面的元素删除g个后,然后来删除 a i a_i ai即可。)
我们离线后,x是递增的,那么这时我们考虑,在不断从前往后减去 a x a_x ax元素影响后,怎么维护可删除点。
首先我们使用线段树维护数组:
v a l [ i ] = { I N F   c n t i < i − a i ∨ a i > i c n t i − ( i − a i )   e l s e val[i]=\left\{ \begin{aligned} INF&&\ cnt_i<i-a_i\lor a_i>i\\ cnt_i-(i-a_i) && \ else \end{aligned} \right. val[i]={INFcnti(iai) cnti<iaiai>i else
其中 c n t i cnt_i cnti是第i个元素之前的可以删除的元素个数。
我们在考虑减去 a x a_x ax的影响时,应该这么做:如果 a x a_x ax是一个不可删除元素,则直接跳过。
如果 a x a_x ax是个可删除元素,那么我们令其变成一个不可删除元素,令 v a l [ x ] = I N F val[x]=INF val[x]=INF,然后将数组 v a l val val区间 [ x + 1 , n ] [x+1,n] [x+1,n]的值减一。然后寻找 v a l val val数组中第一个小于0的数的位置 a k a_k ak,这个数一定是个可删除元素(不可删除元素的val都是INF)。我们把 a k a_k ak设置为不可删除元素,令 v a l [ k ] = I N F val[k]=INF val[k]=INF,然后将 v a l val val的区间 [ k + 1 , n ] [k+1,n] [k+1,n]减一。重复上述过程,直到 v a l val val中不存在小于0的元素。然后通过树状数组查询 [ x , y ] [x,y] [x,y]区间中有多少可删除元素即可。因为每个元素至多删除一次。所以时间复杂度为 O ( n l o g n + m ) O(nlogn+m) O(nlogn+m)
下面是ac代码:

// % everyone
#include <cstdio>
#include<iostream>
#include<cstring>
#include <map>
#include <queue>
#include <set>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <cctype>
#include <time.h>
 
namespace IO {
    double start_time = 0.0;
    void ct() { start_time = clock(); return; }
    void fast_cin() { std::ios::sync_with_stdio(false); std::cin.tie(); }
    void read_f(int flag = 0) { freopen("0.in", "r", stdin); if(!flag) freopen("0.out", "w", stdout); }
    void run_time() { std::cout << "\nESC in : " << ( clock() - start_time ) * 1000.0 / CLOCKS_PER_SEC << "ms" << std::endl; }
}
using namespace IO;
template <typename T>
bool bacmp(const T & a, const T & b) { return a > b; }
template <typename T>
bool pecmp(const T & a, const T & b) { return a < b; }
 
#define ll long long
#define ull unsigned ll
#define _min(x, y) ((x)>(y)?(y):(x))
#define _max(x, y) ((x)>(y)?(x):(y))
#define max3(x, y, z) ( max( (x), max( (y), (z) ) ) )
#define min3(x, y, z) ( min( (x), min( (y), (z) ) ) )
#define pr make_pair
#define pb push_back
using namespace std;

const int N = 3e5+5;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;

struct Node
{
    int l, r;
    int val, pos;
    int laz;
} tr[N<<2];
int su[N], val[N], c[N];
bool vis[N];
int n, m; 
void add(int x, int val)
{
    for (; x < N; x += x & -x) c[x] += val;
}
int askc(int x)
{
    if (x == 0) return 0;
    int res = 0;
    for (; x; x -= x & -x) res += c[x];
    return res;
}
inline void update(int p, int v)
{
    tr[p].val += v;
    tr[p].laz += v;
}
inline void spread(int p)
{
    if (tr[p].laz == 0) return;
    update(p<<1, tr[p].laz);
    update(p<<1|1, tr[p].laz);
    tr[p].laz = 0;
}
inline void pushup(int p)
{
    if (tr[p<<1].val <= tr[p<<1|1].val)
    {
        tr[p].val = tr[p<<1].val;
        tr[p].pos = tr[p<<1].pos;
    }
    else
    {
        tr[p].val = tr[p<<1|1].val;
        tr[p].pos = tr[p<<1|1].pos;
    }
}
void build(int p, int l, int r)
{
    tr[p].l = l; tr[p].r = r;
    tr[p].laz = 0;
    if (l == r)
    { tr[p].val = val[l]; tr[p].pos = l; return; }
    int mid = (l + r) >> 1;
    build(p<<1, l, mid);
    build(p<<1|1, mid+1, r);
    pushup(p);
}
void changek(int p, int pos, int v)
{
    if (tr[p].l == tr[p].r)
    {
        tr[p].val = v;
        return;
    }
    spread(p);
    int mid = (tr[p].l +tr[p].r) >> 1;
    if (pos <= mid) changek(p<<1, pos, v);
    else changek(p<<1|1, pos, v);
    pushup(p);
    return;
}
void change(int p, int l, int r, int v)
{
    if (l > r) return;
    if (l<=tr[p].l && tr[p].r <= r)
    {
        update(p, v); return;
    }
    spread(p);
    int mid = (tr[p].l + tr[p].r) >> 1;
    if (l <= mid) change(p<<1, l, r, v);
    if (r > mid) change(p<<1|1, l, r, v);
    pushup(p);
}
int ask(int p)
{
    if (tr[p].l == tr[p].r) 
        return tr[p].pos;
    spread(p);
    if (tr[p<<1].val < 0) return ask(p<<1);
    else if (tr[p<<1|1].val < 0) return ask(p<<1|1);
    return -1;
}
struct Q
{
    int l, r, id;
} q[N];
bool cmp (const Q & a, const Q & b)
{
    return a.l < b.l;
} 
int ans[N];
void del(int pos)
{
    if (vis[pos] == 0) return;
    vis[pos] = 0; add(pos, -1);
    changek(1, pos, inf);
    change(1, pos+1, n, -1);
    while(tr[1].val < 0)
    {
        int k = ask(1);
        vis[k] = 0; add(k, -1);
        changek(1, k, inf);
        change(1, k+1, n, -1);
    }
    return;
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)    
        scanf("%d", &su[i]);
    int cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        if (su[i] > i) {val[i] = inf; continue;} 
        int g = i - su[i];
        if (g > cnt) {val[i] = inf; continue;}
        val[i] = cnt - g;
        cnt++; add(i, 1); vis[i] = 1;
    }
    build(1, 1, n);
    for (int i = 1; i <= m; i++)
    {
        int x, y; scanf("%d%d", &x, &y);
        q[i] = Q{x, y, i};
    }
    sort(q+1, q+m+1, cmp);
    int mx = 1;
    for (int i = 1; i <= m; i++)
    {
        while(mx <= q[i].l) del(mx++);
        ans[q[i].id] = askc(n - q[i].r) - askc(q[i].l);
    }
    for (int i = 1; i <= m; i++)
        printf("%d\n", ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值