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

Posted Kalzn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树/树状数组(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\\ \\beginaligned INF&&\\ cnt_i<i-a_i\\lor a_i>i\\\\ cnt_i-(i-a_i) && \\ else \\endaligned \\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 以上是关于线段树/树状数组(codeforces 1404C Fixed Point Removal)的主要内容,如果未能解决你的问题,请参考以下文章

非原创codeforces 1070C Cloud Computing 线段树&树状数组

[树状数组][权值线段树] Codeforces 1093E Intersection of Permutations

Codeforces 889F Letters Removing(二分 + 线段树 || 树状数组)

Codeforces 1139F Dish Shopping 树状数组套平衡树 || 平衡树

Codeforces986E Prince's Problem 虚树可持久化线段树树状数组

Sereja and Array-数组操作或者线段树或树状数组