权值线段树套线段树模板
区间每个可重集插入一个数
把权值放外边,内部维护区间
在权值线段树上二分,内部查询数量
\(\text{Code}\)
#include <cstdio>
#include <iostream>
#define LL long long
using namespace std;
const int N = 5e4;
int n, m, rt, seg_size, tr_size;
struct segment{int ls, rs, tg; LL s;}seg[N * 270 + 5];
struct tree{int ls, rs, rt;}T[N * 8 + 5];
inline void pushdown(int p, int tl, int tr)
{
if (!seg[p].tg) return;
LL v = seg[p].tg;
if (!seg[p].ls) seg[p].ls = ++seg_size;
if (!seg[p].rs) seg[p].rs = ++seg_size;
int mid = (tl + tr) >> 1;
seg[seg[p].ls].s += v * (mid - tl + 1), seg[seg[p].ls].tg += v;
seg[seg[p].rs].s += v * (tr - mid), seg[seg[p].rs].tg += v;
seg[p].tg = 0;
}
void update(int &p, int tl, int tr, int l, int r)
{
if (!p) p = ++seg_size;
seg[p].s += min(tr, r) - max(tl, l) + 1;
if (l <= tl && tr <= r) return void(++seg[p].tg);
pushdown(p, tl, tr);
int mid = (tl + tr) >> 1;
if (l <= mid) update(seg[p].ls, tl, mid, l, r);
if (r > mid) update(seg[p].rs, mid + 1, tr, l, r);
}
LL query(int p, int tl, int tr, int l, int r)
{
if (!p || (l <= tl && tr <= r)) return seg[p].s;
pushdown(p, tl, tr);
int mid = (tl + tr) >> 1; LL res = 0;
if (l <= mid) res = query(seg[p].ls, tl, mid, l, r);
if (r > mid) res += query(seg[p].rs, mid + 1, tr, l, r);
return res;
}
void Update(int &p, int tl, int tr, int l, int r, LL k)
{
if (!p) p = ++tr_size;
update(T[p].rt, 1, n, l, r);
if (tl == tr) return;
int mid = (tl + tr) >> 1;
if (k <= mid) Update(T[p].ls, tl, mid, l, r, k);
else Update(T[p].rs, mid + 1, tr, l, r, k);
}
int Query(int p, int tl, int tr, int l, int r, LL k)
{
if (tl == tr) return tl;
int mid = (tl + tr) >> 1;
LL sum = query(T[T[p].rs].rt, 1, n, l, r);
if (k <= sum) return Query(T[p].rs, mid + 1, tr, l, r, k);
return Query(T[p].ls, tl, mid, l, r, k - sum);
}
int main()
{
scanf("%d%d", &n, &m);
int l, r, op; LL k;
for(; m; --m)
{
scanf("%d%d%d%lld", &op, &l, &r, &k);
if (op == 1) Update(rt, -n, n, l, r, k);
else printf("%d\n", Query(rt, -n, n, l, r, k));
}
}