You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 lines
4.9 KiB

2 years ago
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
//快读
LL read() {
LL x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
const int N = 200010;
LL ans[N]; //每个询问的答案
int n, m; //原始n个数字共m个操作
//线段树结构体
#define ls u << 1
#define rs u << 1 | 1
struct Node {
int l, r; //区间范围
LL lazy; //区间修改懒标记
LL sum; //区间和
} tr[N << 2];
//向上更新统计信息:区间和
void pushup(int u) {
tr[u].sum = tr[ls].sum + tr[rs].sum;
}
//向下传递区间修改标记
void pushdown(int u) {
//如果懒标记为0,没有可以传递
if (!tr[u].lazy) return;
//向下传递lazy标志
tr[ls].lazy += tr[u].lazy, tr[rs].lazy += tr[u].lazy; //加法有叠加性
//用lazy通过计算更新左右儿子的sum值
int l = tr[u].l, r = tr[u].r;
int mid = (l + r) >> 1;
tr[ls].sum += tr[u].lazy * (mid - l + 1);
tr[rs].sum += tr[u].lazy * (r - mid);
// lazy标记清零
tr[u].lazy = 0;
}
//构建线段树
void build(int u, int l, int r) {
tr[u] = {l, r, 0, 0};
if (l == r) return;
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
}
//区间更新
//之所以参数起名为[ql,qr]因为这是查询区间,而不是构建区间
void modify(int u, int ql, int qr, LL c) {
int l = tr[u].l, r = tr[u].r; //将变量名l,r保留下来给tr[u].l,tr[u].r
if (ql <= l && qr >= r) { // u结点的管辖范围[tr[u].l~tr[u].r]完整命中
tr[u].lazy += c; //区间修改加上lazy标记,这样就不用现在就逐层下传了
tr[u].sum += c * (r - l + 1); //对统计信息进行更新,完成本层的统计信息更新
return;
}
//下面要进行分裂分裂前需要pushdown
pushdown(u);
int mid = (l + r) >> 1;
if (ql <= mid) modify(ls, ql, qr, c); //与左儿子区间有交集,递归左儿子
if (mid < qr) modify(rs, ql, qr, c); //与右儿子区间有交集,递归右儿子
//向上更新统计信息
pushup(u);
}
//查询区间sum和
LL query(int u, int ql, int qr) {
int l = tr[u].l, r = tr[u].r;
if (ql <= l && r <= qr) return tr[u].sum; //完整命中,返回区间和
LL res = 0;
//分裂前需要pushdown
pushdown(u);
int mid = (l + r) >> 1;
if (ql <= mid) res += query(ls, ql, qr); //与左儿子区间有交集,递归左儿子
if (mid < qr) res += query(rs, ql, qr); //与右儿子区间有交集,递归右儿子
return res;
}
//整体二分的查询结构体
struct Q {
int op, l, r;
LL k;
int id;
} q[N], q1[N], q2[N];
//整体二分
void solve(int l, int r, int ql, int qr) {
if (ql > qr) return; //无效查询区间防RE
if (l == r) {
for (int i = ql; i <= qr; i++)
if (q[i].op == 2) ans[q[i].id] = l; //二分标识答案
return;
}
int mid = (l + r) >> 1;
int nl = 0, nr = 0;
for (int i = ql; i <= qr; i++) {
if (q[i].op == 1) { //增加
if (q[i].k > mid) {
modify(1, q[i].l, q[i].r, 1); //用线段树指定区全都+1
q1[++nl] = q[i]; //加入左增加操作
} else
q2[++nr] = q[i]; //加入右增加操作
} else { //查询
LL t = query(1, q[i].l, q[i].r);
if (t >= q[i].k)
q1[++nl] = q[i];
else {
q[i].k -= t;
q2[++nr] = q[i];
}
}
}
//有加就有减
for (int i = ql; i <= qr; i++)
if (q[i].op == 1)
if (q[i].k > mid) modify(1, q[i].l, q[i].r, -1);
//合并到q数组
for (int i = ql; i < ql + nl; i++) q[i] = q1[i - ql + 1];
for (int i = ql + nl; i <= qr; i++) q[i] = q2[i - ql - nl + 1];
//递归左右
solve(mid + 1, r, ql, ql + nl - 1), solve(l, mid, ql + nl, qr);
}
int main() {
//原序列n个数字共m个操作
n = read(), m = read();
//构建线段树
build(1, 1, n);
int qc = 0; //查询的个数
for (int i = 1; i <= m; i++) {
int op = read(), l = read(), r = read(), k = read();
if (op == 2) //查询操作
q[i] = {op, l, r, k, ++qc};
else // 增加操作 op=1
// 1 l r k : op=1 增加,[l,r]:增加k
q[i] = {op, l, r, k};
}
//整体二分
solve(-1e9, 1e9, 1, m);
//输出
for (int i = 1; i <= qc; i++) printf("%lld\n", ans[i]);
return 0;
}