2020 CCPC Wannafly Winter Camp Day1 I. K小数查询

做法:区间线段树套权值线段树
本题细节有点多,调了一下午,心态爆炸。
修改操作:把区间>x的数全部拎出来并单点修改他们在整个树上的值,并修改x处的值(所有修改都要定位到区间内),注意要更新到所有祖先节点。向下传标记的时候,就不用传到祖先节点了,只需要改自己的内层线段树。
查询操作:把所有子区间拎出来,然后在上面二分即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
#define fi first
#define se second
#define pb push_back
#define mid (l+r>>1)
#define ls o<<1
#define rs o<<1|1
int n, m, a[N], T[N << 2];
int cnt;
int Pint[N * 400], la;
int L[N * 400], R[N * 400], t[N * 400], laz[N * 400];
int newpoint() {
  if (la)return Pint[la--];
  ++cnt;
  L[cnt] = R[cnt] = t[cnt] = 0;
  return cnt;
}
void add(int &o, int l, int r, int d, int da) {
  if (!o)o = newpoint();
  t[o] += da;
  if (l == r)return ;
  if (d <= mid)add(L[o], l, mid, d, da);
  else add(R[o], mid + 1, r, d, da);
}
int need = 0;
void dele(int &o, int l, int r, int x) {//不用传给祖先,只需要改自己就行了
  if (r <= x)return ;
  if (l == r) {
    if (l > x && t[o]) {
      need += t[o];
      t[o] = 0;
      Pint[++la] = o;
      L[o] = R[o] = 0;//空间回收 注意要置0 
      o = 0;
    }
    return ;
  }
  if (t[L[o]])dele(L[o], l, mid, x);
  if (t[R[o]])dele(R[o], mid + 1, r, x);
  t[o] = t[L[o]] + t[R[o]];
}
void push_down(int o, int l, int r) {
  if (laz[ls] > laz[o]) {
    laz[ls] = min(laz[ls], laz[o]);
    need = 0;
    dele(T[ls], 1, n, laz[ls]);//单点修改
    add(T[ls], 1, n, laz[ls], need);
  }
  if (laz[rs] > laz[o]) {
    laz[rs] = min(laz[rs], laz[o]);
    need = 0;
    dele(T[rs], 1, n, laz[rs]);
    add(T[rs], 1, n, laz[rs], need);
  }
}
void build(int o, int l, int r, int x, int y, int da, int num, int sta = 0) {
  if (x < l || y > r)return ;
  if (x >= l && y <= r) {add(T[o], 1, n, num, da);}
  if (l == x && r == y)return ;
  push_down(o, l, r);
  build(ls, l, mid, x, y, da, num, sta);
  build(rs, mid + 1, r, x, y, da, num, sta);
}
int Fi[N*400],Se[N*400];
int dag=0;
void find(int o, int l, int r, int d) {//找到所有>d的节点
  if (l == r) {
    if (t[o] && l > d){
      ++dag;
      Fi[dag]=l;
      Se[dag]=o;
    }
    return ;
  }
  if (d < mid && t[L[o]])find(L[o], l, mid, d);
  if (t[R[o]])find(R[o], mid + 1, r, d);
}
void find(int o, int l, int r, int x, int y, int d) {
  if (d > laz[o])return ;
  if (l >= x && r <= y) {
    laz[o] = min(laz[o], d);
    dag=0;
    find(T[o], 1, n, laz[o]);
    for(int i=1;i<=dag;i++){//更新这些节点
      int fa=t[Se[i]];
      build(1, 1, n, l, r, -fa, Fi[i], 0);
      build(1, 1, n, l, r, fa, laz[o], 0);
    }
    return ;
  }
  push_down(o, l, r);
  if (x <= mid)find(ls, l, mid, x, y, d);
  if (y > mid)find(rs, mid + 1, r, x, y, d);
}
int Pos[N*400],po;
void find_pos(int o, int l, int  r, int x, int y) {
  if (l >= x && r <= y) {
    Pos[++po]=T[o];
    return ;
  }
  push_down(o, l, r);
  if (x <= mid)find_pos(ls, l, mid, x, y);
  if (y > mid)find_pos(rs, mid + 1, r, x, y);
}
int get_ans(int x, int y, int d) {
  if (x == y)return x;
  int an = 0;
  for(int i=1;i<=po;i++)an+=t[L[Pos[i]]];
  if (an >= d) {
    for(int i=1;i<=po;i++)Pos[i]=L[Pos[i]];
    return get_ans(x, (x + y) >> 1, d);
  } else {
    for(int i=1;i<=po;i++)Pos[i]=R[Pos[i]];
    return get_ans(((x + y) >> 1) + 1, y, d - an);
  }
}
int main() {
  scanf("%d%d", &n, &m);
  memset(laz, 0x3f3f3f3f, sizeof laz);
  for (int i = 1; i <= n; i++)scanf("%d", a + i), build(1, 1, n, i, i, 1, a[i]);
  for (int i = 1; i <= m; i++) {
    int o, l, r, x;
    scanf("%d%d%d%d", &o, &l, &r, &x);
    if (o == 1) {
      find(1, 1, n, l, r, x);
    } else {
      po=0;
      find_pos(1, 1, n, l, r);
      printf("%d\n", get_ans(1, n, x));
    }
  }
  return 0;
}
发布了173 篇原创文章 · 获赞 94 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40655981/article/details/104447318