先讲个故事。。。
据说某一天,claris扔了一道题到某群里面然后引起了不大的讨论~然后好学向上的whx同学发现了这题。。。聪明的whx想了很久。。。然后!whx发现看不懂claris给的暴力的证明。。。于是去找了吉司机。。。吉司机若有所思。。。再后来跟whx说:这是对的。。。然而whx同学并没有写。。。最后就弃坑啦。。。
第二年,whx来到WC2016的会场上,发现:诶,吉司机的线段树怎么这么。。。眼熟?
自行脑补
咳咳,回到正题。
去了WC2016的都知道这类取max的题的做法。
容易发现线段树直接做不太好做,尝试深入发掘一下线段树的性质。
假设我们需要对 i∈[l,r] 进行 Ai→max{Ai,x} 的操作,考虑用线段树瞎搞。对每个节点 u 维护最小值 mn[u] 和次小值 se[u] ,那么我们可以对 v 进行分类讨论。
当 v<=mn[u] 时不鸟它。当 mn[u]<v<se[u] 时我们可以打一个“最小值的cover标记”,把最小值都给设成 v 。当 v>=se[u] 的时候继续暴力修改下去直到当前区间满足条件。jry和lzz的营员交流证明了这个做法总复杂度是 O((m+n)logn) 的。然而总是有一种这玩意是两个log的错觉
于是到这道题里面就差不多地搞了。首先我们可以把操作2归约成一个最小值增加操作和区间增加操作。然后操作1可以变成 max{Ai−inf,0} ,再区间加上 C 。注意这个做法不小心的话会爆掉long long。还有一种做法是增加一个区间cover标记,这样常数会小一些。操作3直接搞就好了。
注意代码实现的时候有不少的坑点,比如最小值增量标记必须在 mn[u]<v<se[u] 的时候打,下传标记的时候要注意判一下最小值是否相等。
复杂度 O((n+m)logn) 。
#include
using namespace std;
#define rep(i,a,b) for (int i = a , _ = b;i <= _;i ++)
#define maxn 300007
inline int rd() {
char c = getchar();
while (!isdigit(c) && c != '-') c = getchar() ; int x = 0 , f = 1;
if (c == '-') f = -1 ; else x = c - '0';
while (isdigit(c = getchar())) x = x * 10 + c - '0';
return x * f;
}
const int maxs = 1500007;
typedef long long ll;
typedef ll seg[maxs];
typedef int arr[maxn];
arr a;
int n , m;
#define lc (u << 1)
#define rc (u << 1 | 1)
#define T int u = 1 , int l = 1 , int r = n
#define U u , l , r
#define L lc , l , m
#define R rc , m + 1 , r
#define current ql <= l && r <= qr
const ll inf = 1000000000000ll;
static int DBG_CLOCK = 0;
#define ASS //printf("current time %d\n" , ++ DBG_CLOCK),assert(sum[u] > 0);
struct SegTree {
ll tag[maxs][3];
seg mn , se , sum;
int ql , qr;
ll v;
//tag0 : cover ; tag1 : add ; tag3 : delta of minimum
inline void mt(int u) {
mn[u] = min(mn[lc] , mn[rc]);
if (mn[lc] == mn[rc])
sum[u] = sum[lc] + sum[rc] , se[u] = min(se[lc] , se[rc]);
if (mn[lc] != mn[rc]) {
if (mn[lc] > mn[rc])
se[u] = min(mn[lc] , se[rc]) , sum[u] = sum[rc];
else
se[u] = min(mn[rc] , se[lc]) , sum[u] = sum[lc];
}
ASS
}
inline void mark_cover(ll v , T) {
tag[u][0] = v;
mn[u] = v , se[u] = inf;
tag[u][1] = tag[u][2] = 0;
sum[u] = r - l + 1;
// printf("%d %d %d\n" , u , l , r);
ASS
}
inline void mark_add(ll v , T) {
tag[u][1] += v;
mn[u] -= v;
if (se[u] != inf) se[u] -= v;
}
inline void mark_delta(ll v , T) {
tag[u][2] += v;
mn[u] -= v;
}
inline bool mark_modi(T) {
if (v <= mn[u]) {
mark_add(v , U);
} else if (mn[u] < v && v < se[u]) { // v !!!!!= se[u]
mark_delta(mn[u] - v , U);
mark_add(v , U);
} else { // v >= se[u] , modify forcely
if (l == r)
return (mn[u] = 0 , se[u] = inf , sum[u] = 1) , 1;
else
return 0;
}
return 1;
}
inline void push(T) {
int m = (l + r) >> 1;
if (tag[u][0] != -1) {
mark_cover(tag[u][0] , L) , mark_cover(tag[u][0] , R);
tag[u][0] = -1;
}
if (tag[u][1]) {
mark_add(tag[u][1] , L) , mark_add(tag[u][1] , R);
tag[u][1] = 0;
}
if (tag[u][2]) {
if (mn[lc] - tag[u][2] == mn[u]) mark_delta(tag[u][2] , L);
if (mn[rc] - tag[u][2] == mn[u]) mark_delta(tag[u][2] , R);
tag[u][2] = 0;
}
}
void build(T) {
tag[u][0] = -1;
if (l == r) {
mn[u] = a[l] , se[u] = inf;
sum[u] = 1;
return;
}
int m = (l + r) >> 1;
build(L) , build(R);
mt(u);
}
int que(T) {
if (l != r)
push(U);
if (current)
return mn[u] == 0 ? sum[u] : 0;
int m = (l + r) >> 1 , t = 0;
if (ql <= m) t += que(L);
if (qr > m) t += que(R);
return t;
}
void cover(T) {
if (l != r)
push(U);
if (current)
{ mark_cover(v , U) ; return ; }
int m = (l + r) >> 1;
if (ql <= m) cover(L);
if (qr > m) cover(R);
mt(u);
}
void modi(T) {
if (l != r)
push(U);
if (current)
if (mark_modi(U))
return;
int m = (l + r) >> 1;
if (ql <= m) modi(L);
if (qr > m) modi(R);
mt(u);
}
inline int Q(int l , int r) {
ql = l , qr = r;
return que();
}
inline void S(int l , int r , ll w) {
ql = l , qr = r , v = w;
cover();
}
inline void M(int l , int r , ll w) {
ql = l , qr = r , v = w;
modi();
}
inline void B() {
build();
}
}num;
void input() {
n = rd() , m = rd();
rep (i , 1 , n) a[i] = rd();
num.B();
}
void SetVal(int l , int r) {
int v = rd();
// num.M(l , r , -inf + 1);
// num.M(l , r , v);
num.S(l , r , v);
}
void Change(int l , int r) {
int v = - rd();
num.M(l , r , v);
}
void query(int l , int r) {
int ans = num.Q(l , r);
printf("%d\n" , ans);
}
void solve() {
rep (i , 1 , m) {
int t = rd() , l = rd() , r = rd();
if (t == 1)
SetVal(l , r);
else if (t == 2)
Change(l , r);
else
query(l , r);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("data.txt" , "r" , stdin);
freopen("data.out" , "w" , stdout);
#endif
input();
solve();
return 0;
}