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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章