题意:
给你一个数组a长度为n,有三种操作:
- 0 x y t:
- 1 x y:求
- 2 x,y:求
刚刚结束的字节跳动camp出了一道取最小值的差不多的题,当时不会,现在来补一下。
- 对于一棵线段树我们存一下每个节点管辖区域的最大值mx,次大值se还有最大值的数量cnt。
- 每次更新的时候,会有三种情况
,管辖区域内全部值不会受本次更新影响;
,管辖区域内只有mx值受到本次更新影响,我们只要把mx更新到t,sum减去mx和t的差值乘上cnt即可;
,管辖区域内有若干个值受到本次更新影响,那么我们直接暴力递归更新;
- 重复以上操作。
- 然后操作1和操作2,就是一颗线段树的普通用法了。
接下来,口胡证明一下为什么对于情况三我们暴力更新的时间复杂度是正确的。
- 我们想一下可以知道,我们更新的复杂度主要来源于对不同的最大值标志的回收与更新。
- 然后,我们想象一下一棵正常的线段树,易知一棵线段树的最大值标志最多有n种。而且深度随数值减小递增,最多有
层。
- 一次更新的最大复杂度是把全部标志回收,时间复杂度为
。
然后可能会以为,这不对呀,这不就吧这个做法证伪了吗。 - 我们先认为这棵线段树的潜在复杂度是
的,然而我们每次更新会消去若干个最大值标志。并且最多把最大值标志的数两+1,潜在的时间复杂度增加了
。所以m次操作后的的总时间复杂度还是
级别的。
#include<algorithm>
#include<vector>
#include<iostream>
#include<math.h>
#include<cstring>
#include<string>
#include<stack>
#include<map>
#include<set>
#include<unordered_map>
#include<queue>
#include<assert.h>
#include <iomanip>
#define qcin; ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define pb push_back
#define mp make_pair
#define clr(x) memset(x,0,sizeof x)
#define fmax(x) memset(x,0x3f,sizeof x)
#define finit(x) memset(x,-1,sizeof x)
#define iio(n,m) io(n),io(m)
#define ls(p) (p<<1)
#define rs(p) ((p<<1)|1)
#define dis(l,r) r-l+1
#define gstr(str) scanf("%s",str)
#define glen(str) strlen(str)
using namespace std;
namespace Input{
const int BUF = 65536;
char buf[BUF + 1];
char *head = buf, *tail = buf;
}
inline char inputchar(){
using namespace Input;
if(head == tail)
*(tail = (head = buf) + fread(buf, 1, BUF, stdin)) = 0;
return *head++;
}
template<class T>
inline void io(T &ret){
char ch = inputchar();
while(ch < '0' || ch > '9')
ch = inputchar();
ret = ch - '0';
ch = inputchar();
while(ch >= '0' && ch <= '9'){
ret = ret * 10 + ch - '0';
ch = inputchar();
}
}
typedef long long ll;
typedef pair<ll,ll>pll;
const int maxn = 5e6+10;
const int mod = 1e8+7;
const ll inf = 1e18;
typedef ll arr[maxn];
typedef char str[maxn];
void file(int x){if(x&&fopen("123.in","r")){freopen("123.in","r",stdin);}}
const long double pi = acos(-1);
int n,m,ql,qr,op,t;
ll val;
struct node{
ll sum,cnt,mx,se;
}T[maxn];
arr a;
void push_up(int p){
T[p].sum=T[ls(p)].sum+T[rs(p)].sum;
if(T[ls(p)].mx>T[rs(p)].mx){
T[p].mx=T[ls(p)].mx;
T[p].cnt=T[ls(p)].cnt;
T[p].se=max(T[ls(p)].se,T[rs(p)].mx);
}else if(T[ls(p)].mx<T[rs(p)].mx){
T[p].mx=T[rs(p)].mx;
T[p].cnt=T[rs(p)].cnt;
T[p].se=max(T[rs(p)].se,T[ls(p)].mx);
}else{
T[p].mx=T[ls(p)].mx;
T[p].cnt=T[ls(p)].cnt+T[rs(p)].cnt;
T[p].se=max(T[ls(p)].se,T[rs(p)].se);
}
}
void push_down(int p){
int mx=T[p].mx;
if(mx<T[ls(p)].mx){
T[ls(p)].sum-=T[ls(p)].cnt*(T[ls(p)].mx-mx);
T[ls(p)].mx=mx;
}
if(mx<T[rs(p)].mx){
T[rs(p)].sum-=T[rs(p)].cnt*(T[rs(p)].mx-mx);
T[rs(p)].mx=mx;
}
}
void build(int p,int l,int r){
if(l==r){
T[p].cnt=1;
T[p].sum=T[p].mx=a[l];
T[p].se=-1;
return;
}
int md=(l+r)>>1;
build(ls(p),l,md);
build(rs(p),md+1,r);
push_up(p);
}
void update(int p,int l,int r,ll val){
if(val>=T[p].mx)return;
if(ql<=l&&qr>=r&&val>T[p].se){
T[p].sum-=T[p].cnt*(T[p].mx-val);
T[p].mx=val;
return;
}
int md=(l+r)>>1;
push_down(p);
if(md>=ql)update(ls(p),l,md,val);
if(md<qr)update(rs(p),md+1,r,val);
push_up(p);
}
ll qry_max(int p,int l,int r){
if(ql<=l&&qr>=r){
return T[p].mx;
}
ll res=0,md=(l+r)>>1;
push_down(p);
if(md>=ql)res=max(res,qry_max(ls(p),l,md));
if(md<qr)res=max(res,qry_max(rs(p),md+1,r));
return res;
}
ll qry_sum(int p,int l,int r){
if(ql<=l&&qr>=r){
return T[p].sum;
}
ll res=0,md=(l+r)>>1;
push_down(p);
if(md>=ql)res+=qry_sum(ls(p),l,md);
if(md<qr)res+=qry_sum(rs(p),md+1,r);
return res;
}
int main(){
file(1);
for(io(t);t--;){
iio(n,m);
for(int i=1;i<=n;i++){
io(a[i]);
}
T[0]={0,0,0,-1};
build(1,1,n);
while(m--){
io(op),iio(ql,qr);
if(!op){
io(val);
update(1,1,n,val);
}else if(op==1){
printf("%lld\n",qry_max(1,1,n));
}else if(op==2){
printf("%lld\n",qry_sum(1,1,n));
}
}
}
return 0;
}