扶苏的问题
题目描述
给定一个长度为 n n n 的序列 a a a,要求支持如下三个操作:
- 给定区间 [ l , r ] [l, r] [l,r],将区间内每个数都修改为 x x x。
- 给定区间 [ l , r ] [l, r] [l,r],将区间内每个数都加上 x x x。
- 给定区间 [ l , r ] [l, r] [l,r],求区间内的最大值。
输入格式
第一行是两个整数,依次表示序列的长度
n
n
n 和操作的个数
q
q
q。
第二行有
n
n
n 个整数,第
i
i
i 个整数表示序列中的第
i
i
i 个数
a
i
a_i
ai。
接下来
q
q
q 行,每行表示一个操作。每行首先有一个整数
o
p
op
op,表示操作的类型。
- 若 o p = 1 op = 1 op=1,则接下来有三个整数 l , r , x l, r, x l,r,x,表示将区间 [ l , r ] [l, r] [l,r] 内的每个数都修改为 x x x。
- 若 o p = 2 op = 2 op=2,则接下来有三个整数 l , r , x l, r, x l,r,x,表示将区间 [ l , r ] [l, r] [l,r] 内的每个数都加上 x x x。
- 若 o p = 3 op = 3 op=3,则接下来有两个整数 l , r l, r l,r,表示查询区间 [ l , r ] [l, r] [l,r] 内的最大值。
输出格式
对于每个 o p = 3 op = 3 op=3 的操作,输出一行一个整数表示答案。
样例 #1
样例输入 #1
6 6
1 1 4 5 1 4
1 1 2 6
2 3 4 2
3 1 4
3 2 3
1 1 6 -1
3 1 6
样例输出 #1
7
6
-1
样例 #2
样例输入 #2
4 4
10 4 -3 -7
1 1 3 0
2 3 4 -4
1 2 4 -9
3 1 4
样例输出 #2
0
提示
数据规模与约定
- 对于 10 % 10\% 10% 的数据, n = q = 1 n = q = 1 n=q=1。
- 对于 40 % 40\% 40% 的数据, n , q ≤ 1 0 3 n, q \leq 10^3 n,q≤103。
- 对于 50 % 50\% 50% 的数据, 0 ≤ a i , x ≤ 1 0 4 0 \leq a_i, x \leq 10^4 0≤ai,x≤104。
- 对于 60 % 60\% 60% 的数据, o p ≠ 1 op \neq 1 op=1。
- 对于 90 % 90\% 90% 的数据, n , q ≤ 1 0 5 n, q \leq 10^5 n,q≤105。
- 对于 100 % 100\% 100% 的数据, 1 ≤ n , q ≤ 1 0 6 1 \leq n, q \leq 10^6 1≤n,q≤106, 1 ≤ l , r ≤ n 1 \leq l, r \leq n 1≤l,r≤n, o p ∈ { 1 , 2 , 3 } op \in \{1, 2, 3\} op∈{1,2,3}, ∣ a i ∣ , ∣ x ∣ ≤ 1 0 9 |a_i|, |x| \leq 10^9 ∣ai∣,∣x∣≤109。
提示
请注意大量数据读入对程序效率造成的影响。
思路
这道题涉及修改数字和求数字和,此时我们就得用懒标记。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+10 , INT = LONG_LONG_MIN;
struct E{
int l,r;
int maxv;
int add;//增加的懒标记
int change;//改变的懒标记
}tr[N*4];
int n,m;
int w[N];
void pushup(int u){
tr[u].maxv=max(tr[u<<1].maxv,tr[u<<1|1].maxv);
}
//在changedown中没有将加法的懒标记去掉,因为在更改数字后,前面的加法也就无效了
void changedown(int u){
if(tr[u].change!=INT){
tr[u<<1].change=tr[u].change,tr[u<<1|1].change=tr[u].change;
tr[u<<1].maxv=tr[u].change,tr[u<<1|1].maxv=tr[u].change;
tr[u].change=INT;
tr[u<<1].add=tr[u<<1|1].add=0;
}
}
void pushdown(int u){
if(tr[u].add){
tr[u<<1].add+=tr[u].add,tr[u<<1|1].add+=tr[u].add;
tr[u<<1].maxv+=tr[u].add,tr[u<<1|1].maxv+=tr[u].add;
tr[u].add=0;
}
}
void build(int u,int l,int r){
if(l==r)tr[u]={l,r,w[l],0,INT};
else{
tr[u]={l,r};
tr[u].change=INT;
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
}
void add(int u,int l,int r,int c){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].add+=c;
tr[u].maxv+=c;
return;
}
changedown(u);
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid)add(u<<1,l,r,c);
if(r>mid)add(u<<1|1,l,r,c);
pushup(u);
}
void chang(int u,int l,int r,int c){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].maxv=tr[u].change=c;
tr[u].add=0;
return;
}
changedown(u);
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid)chang(u<<1,l,r,c);
if(r>mid)chang(u<<1|1,l,r,c);
pushup(u);
}
int query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u].maxv;
}
changedown(u);
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;
int v=INT;
if(l<=mid) v=max(query(u<<1,l,r),v);
if(r>mid) v=max(query(u<<1|1,l,r),v);
return v;
}
/*数组开小了,要开到1e6
在changedown中不能判断“ tr[u].change ”不为零,因为有些操作会将区间内的所有数更改成零,建议初始化为负无穷。
在changedown中没有将加法的懒标记去掉,因为在更改数字后,前面的加法也就无效了。
在add中“ tr[u].lazy=”因改为“ tr[u].lazy+=k ”
在add中的“ pushdown ”前加上“ changedown ”判断它的值是否要修改
在memery中“ ans ”的初值应赋值为负无穷而不是“ -INT_MAX ”
读入非常多不能直接用“ cin ”和“ cout ”否则会超时*/
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
build(1,1,n);
while(m--){
int op,l,r,x;
scanf("%lld",&op);
if(op==1){
scanf("%lld%lld%lld",&l,&r,&x);
chang(1,l,r,x);
}else if(op==2){
scanf("%lld%lld%lld",&l,&r,&x);
add(1,l,r,x);
}else{
scanf("%lld%lld",&l,&r);
cout<<query(1,l,r)<<endl;
}
}
return 0;
}