Solution
首先另 L = -n, R = n,那么 mid = (L + R) >> 1.考虑对于 mid 把所有操作(修改 / 查询)分为两类:dl 和 dr,使得两部分互不干扰。这样就可以把原问题拆成两个子问题,从而进行整体二分。
考虑维护一个线段树,表示 [l,r] 区间有多少个数 > mid;
对于修改操作:
如果 c > mid,在线段树上对 [l,r] 整体 +1;这时候该操作可能对右区间产生贡献,因此放入 dr 内;
如果 c <= mid,该操作可能对左区间产生贡献,因此放入 dl 内。
对于查询操作:
在线段树上查询 [l,r] 内有几个数 > mid,记为 res:
如果 c > res,说明该询问的答案应该 < mid,因此把当前询问分入 dl 内;
否则,说明当前询问的答案应该 >= mid,因此把当前询问放入 dr 内。
总结一下,线段树需要支持如下两个操作:区间 +1,区间求和。
其他一些具体细节在代码里(代码是抄的题解 qwq)
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
const LL N = 2333333;
struct Que { LL opt, l, r, c, id; } q[N], dl[N], dr[N];
LL n, m, num = 0, len1 = 0, len2 = 0, ql[N], qr[N], ans[N];
struct SegmentTree
{
LL tag[N], sum[N];
void push_up(LL x) { sum[x] = sum[x << 1] + sum[x << 1 | 1]; }
void push_down(LL x, LL l, LL r)
{
if(tag[x] == 0) return ;
LL mid = (l + r) >> 1;
sum[x << 1] += tag[x] * (mid - l + 1);
sum[x << 1 | 1] += tag[x] * (r - mid);
tag[x << 1] += tag[x], tag[x << 1 | 1] += tag[x];
tag[x] = 0;
}
void update(LL x, LL l, LL r, LL stdl, LL stdr, LL k)
{
if(l > stdr || r < stdl) return ;
if(stdl <= l && stdr >= r)
{
tag[x] += k;
sum[x] += k * (r - l + 1);
push_down(x, l, r);
return ;
}
LL mid = (l + r) >> 1;
push_down(x, l, r);
update(x << 1, l, mid, stdl, stdr, k);
update(x << 1 | 1, mid + 1, r, stdl, stdr, k);
push_up(x), push_down(x, l, r);
}
LL query(LL x, LL l, LL r, LL stdl, LL stdr)
{
if(l > stdr || r < stdl) return 0;
if(stdl <= l && stdr >= r) return sum[x];
LL mid = (l + r) >> 1;
push_down(x, l, r);
return query(x << 1, l, mid, stdl, stdr) +
query(x << 1 | 1, mid + 1, r, stdl, stdr);
}
} tree;
void work(LL st, LL ed, LL l, LL r)
{
LL lenl = 0, lenr = 0, Fl = 0, Fr = 0;
if(l == r)
{
for(LL i = st; i <= ed; i++)
if(q[i].opt == 2) ans[q[i].id] = l;
return ;
}
LL mid = (l + r) >> 1;
for(LL i = st; i <= ed; i++)
{
if(q[i].opt == 1)
if(q[i].c > mid)
{
tree.update(1, 1, n, q[i].l, q[i].r, 1);
dr[++lenr] = q[i];
}
else dl[++lenl] = q[i];
else
{
LL res = tree.query(1, 1, n, q[i].l, q[i].r);
if(res >= q[i].c) Fr = 1, dr[++lenr] = q[i];
else
{
Fl = 1, q[i].c -= res;
dl[++lenl] = q[i];
}
}
}
for(LL i = st; i <= ed; i++)
if(q[i].opt == 1 && q[i].c > mid)
tree.update(1, 1, n, q[i].l, q[i].r, -1);
for(LL i = 1; i <= lenl; i++) q[st + i - 1] = dl[i];
for(LL i = lenl + 1; i <= lenl + lenr; i++) q[st + i - 1] = dr[i - lenl];
if(Fl == 1) work(st, st + lenl - 1, l, mid);
if(Fr == 1) work(st + lenl, ed, mid + 1, r);
}
int main()
{
scanf("%lld%lld", &n, &m);
for(LL i = 1; i <= m; i++)
{
scanf("%lld%lld%lld%lld", &q[i].opt, &q[i].l, &q[i].r, &q[i].c);
if(q[i].opt == 2) q[i].id = ++num;
}
work(1, m, -n, n);
for(LL i = 1; i <= num; i++) printf("%lld
", ans[i]);
return 0;
}