题目链接
题目描述 Description
给一个序列
支持3种操作
0 u v t 对于所有i u<=i<=v a[i]=min(a[i],t)
1 u v 对于所有i u<=i<=v,输出最大的a[i]
2 u v 对于所有i u<=i<=v,输出a[i]的和
输入描述 Input Description
多组测试数据,第一行T表示数据组数
对于每组数据,一行n,m表示长度和操作个数
接下来一行n个数代表序列的值
接下来m行每行一个操作
输出描述 Output Description
对于每一个询问输出一行表示答案
水题分析 Waterproblem Analysis
根据题目的描述以及输入和输出基本上推断出应该是线段树的题目。一开始只记录区间的最大值与区间合,更新时每次都要递归到底,时间复杂度就变成了O(n^2logn)显然是不够的。
那么就要引入吉司机线段树。吉司机线段树的可以用来处理区间最值更新的问题,其主要思想是在维护最大值的同时记录次数和次小值。
当我们在更新区间最值时分成三种情况。
1.如果小于等于要求改的最值,就不需要做处理。
2.如果大于次小值,那么我们就可以根据其与最大值的差值和最大值的个数就可以O(1)的更新答案了,同时打上标记。
3.如果小于等于次小值,再向左右儿子递归。
有了这三条规则,这样才能保证lazy标记下放时直接更新子区间最大值和区间合的正确性,因为要更改的值大于次大值,所以不可能更小,与父亲区间更新的操作相同。
用势能分析的方法可以证明时间复杂度为O(mlogn)。
这道题卡常数,一定要注意代码书写的风格。c++自带的max,min函数跑的是真的很慢。
附上代码:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<cstring>
#define ll long long
#define INF 0x3f3f3f3f
#define EF if(ch==EOF) return x;
using namespace std;
const int maxn=1e6+10;
int n,q,T;
ll cnt[maxn<<2];
ll a[maxn];
ll sum[maxn<<2];
ll maxx[maxn<<2],smaxx[maxn<<2],lazy[maxn<<2];
void pushup(int d) {
sum[d]=sum[d<<1]+sum[d<<1|1];
maxx[d]=max(maxx[d<<1],maxx[d<<1|1]);
smaxx[d]=max(smaxx[d<<1|1],smaxx[d<<1]);
if(maxx[d<<1]!=maxx[d<<1|1]) smaxx[d]=max(smaxx[d],min(maxx[d<<1],maxx[d<<1|1]));
cnt[d]=0;
if(maxx[d]==maxx[d<<1]) {
cnt[d]+=cnt[d<<1];
}
if(maxx[d]==maxx[d<<1|1]) {
cnt[d]+=cnt[d<<1|1];
}
}
void pushdown(int d) {
if(maxx[d<<1]>lazy[d]) {
sum[d<<1]+=1ll*cnt[d<<1]*(lazy[d]-maxx[d<<1]);
lazy[d<<1]=maxx[d<<1]=lazy[d];
}
if(maxx[d<<1|1]>lazy[d]) {
sum[d<<1|1]+=1ll*cnt[d<<1|1]*(lazy[d]-maxx[d<<1|1]);
lazy[d<<1|1]=maxx[d<<1|1]=lazy[d];
}
lazy[d]=-1;
}
void build(int d,int l,int r) {
lazy[d]=-1;
smaxx[d]=-1;
if(l==r) {
sum[d]=maxx[d]=a[l];
cnt[d]=1;
return;
}
int mid=(l+r)>>1;
build(d<<1,l,mid);
build(d<<1|1,mid+1,r);
pushup(d);
}
void modify(int d,int L,int R,int l,int r,ll x) {
if(maxx[d]<=x) return;
if(L==l&&R==r&&smaxx[d]<x) {
sum[d]+=1ll*cnt[d]*(x-maxx[d]);
lazy[d]=x;
maxx[d]=x;
return;
}
if(lazy[d]!=-1) pushdown(d);
int mid=(L+R)>>1;
if(r<=mid) modify(d<<1,L,mid,l,r,x);
else if(l>mid) modify(d<<1|1,mid+1,R,l,r,x);
else {
modify(d<<1,L,mid,l,mid,x);
modify(d<<1|1,mid+1,R,mid+1,r,x);
}
pushup(d);
}
ll query_max(int d,int L,int R,int l,int r) {
if(L==l&&R==r) {
return maxx[d];
}
if(lazy[d]!=-1) pushdown(d);
int mid=(L+R)>>1;
if(r<=mid) return query_max(d<<1,L,mid,l,r);
else if(l>mid) return query_max(d<<1|1,mid+1,R,l,r);
else return max(query_max(d<<1,L,mid,l,mid),query_max(d<<1|1,mid+1,R,mid+1,r));
}
ll query_sum(int d,int L,int R,int l,int r) {
if(L==l&&R==r) {
return sum[d];
}
if(lazy[d]!=-1) pushdown(d);
int mid=(L+R)>>1;
if(r<=mid) return query_sum(d<<1,L,mid,l,r);
else if(l>mid) return query_sum(d<<1|1,mid+1,R,l,r);
else return query_sum(d<<1,L,mid,l,mid)+query_sum(d<<1|1,mid+1,R,mid+1,r);
}
int main() {
for(scanf("%d",&T);T--;) {
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
build(1,1,n);
for(int i=1;i<=q;++i) {
int op,l,r;
ll x;
scanf("%d%d%d",&op,&l,&r);
if(op==0) {
scanf("%lld",&x);
modify(1,1,n,l,r,x);
}
if(op==1) {
printf("%lld\n",query_max(1,1,n,l,r));
}
if(op==2) {
printf("%lld\n",query_sum(1,1,n,l,r));
}
}
}
}