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.

142 lines
4.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <iostream>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 10;
#define ls u << 1
#define rs u << 1 | 1
//快读
int read() {
int 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;
}
struct Node {
int l, r, mx, se, cnt; // 区间最大值mx, 区间次大值se, 区间最大值个数cnt
LL sum; //区间和
} tr[N << 2];
//向上一级推送统计信息
void pushup(int u) {
tr[u].sum = tr[ls].sum + tr[rs].sum;
tr[u].mx = max(tr[ls].mx, tr[rs].mx);
if (tr[ls].mx == tr[rs].mx) { //左右儿子最大值样等
tr[u].cnt = tr[ls].cnt + tr[rs].cnt; //父亲的最大值个数=左儿子最大值个数+右儿子最大值个数
tr[u].se = max(tr[ls].se, tr[rs].se); //次大的,就需要讨论一下左右儿子谁的次大比较大
}
if (tr[ls].mx > tr[rs].mx) { //如果左儿子最大值大于右儿子最大值
tr[u].cnt = tr[ls].cnt; //记录左儿子最大值个数
tr[u].se = max(tr[ls].se, tr[rs].mx); //次大变成左儿子次大值与右儿子最大值PK
}
if (tr[ls].mx < tr[rs].mx) {
tr[u].cnt = tr[rs].cnt;
tr[u].se = max(tr[rs].se, tr[ls].mx);
}
}
//构建线段树
void build(int u, int l, int r) {
// 每个都认真初始化,全部赋值一遍最稳妥
// 此题卡时间卡的比较严重如果不用下面的方法每次memset(tr,0,sizeof(tr));就会TLE
tr[u].l = l, tr[u].r = r, tr[u].mx = tr[u].cnt = tr[u].sum = 0;
tr[u].se = -INF; //初始时没有次大值,最小值是0所以设置成-INF就肯定小于最小值了
if (l == r) {
tr[u].sum = tr[u].mx = read();
tr[u].cnt = 1;
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
//将modify拆分成两个独立出来
void update_min(int u, int v) {
if (tr[u].mx <= v) return; //最大值都没有k大k就是无用的东西
tr[u].sum += ((LL)v - tr[u].mx) * tr[u].cnt; // 区间整体命中,并且比最大值小,能修改的只有最大值
tr[u].mx = v; // 最大值修改为k
}
//向下一级传递懒标记tag标记,此时的懒标记就是tr[u].mx也就是区间最大值
void pushdown(int u) {
update_min(ls, tr[u].mx), update_min(rs, tr[u].mx);
}
void modify_min(int u, int l, int r, int v) {
// ①与修改区间与当前区间无次k>=tr[u].mx 时,没有修改的必要
if (r < tr[u].l || l > tr[u].r || v >= tr[u].mx) return;
// ②整个区间命中不用再分裂并且tr[u].se<k<tr[u].mx update(u,k)将只改最大
if (l <= tr[u].l && tr[u].r <= r && tr[u].se < v) {
update_min(u, v);
return;
}
//③ k <=tr[u].se 时,无法实现只对最大值的修改,只能继续分裂,直到区间可以被上面两种条件覆盖到
pushdown(u); //先下传lazy懒标记,防止有上次的累计懒标记没有处理,造成标记错乱或丢失
//递归左右儿子
modify_min(ls, l, r, v), modify_min(rs, l, r, v);
//叶子节点修改后,别忘了再上传统计信息,比如什么最大值个数,次大值是谁啥的,上面可都没管,但最终修改后要更新一次
pushup(u);
}
//普通线段树的区间最大值
int query_max(int u, int l, int r) {
if (r < tr[u].l || l > tr[u].r) return -INF;
if (l <= tr[u].l && tr[u].r <= r) return tr[u].mx;
pushdown(u);
return max(query_max(ls, l, r), query_max(rs, l, r));
}
//普通线段树的区间和
LL query_sum(int u, int l, int r) {
if (r < tr[u].l || l > tr[u].r) return 0;
if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum; //完全在查询区间内直接返回tr[u].sum
pushdown(u);
return query_sum(ls, l, r) + query_sum(rs, l, r);
}
void solve() {
int n = read(), m = read();
//构建线段树
build(1, 1, n);
while (m--) {
int op = read(), l = read(), r = read();
if (op == 0) {
int v = read();
modify_min(1, l, r, v); //将区间[l,r]中a[i](i∈[l,r]),与x取min,最后将整个区间修改为min(x,a[i])
}
if (op == 1) printf("%d\n", query_max(1, l, r)); //查询区间最大值
if (op == 2) printf("%lld\n", query_sum(1, l, r)); // 查询区间和
}
}
int main() {
//文件输入输出
#ifndef ONLINE_JUDGE
freopen("HDU5306.in", "r", stdin);
#endif
int T = read();
for (int i = 1; i <= T; i++) solve(); //多组测试数据提取solve函数然后执行T次是一个好方法
return 0;
}