隐藏
「HDU5306」Gorgeous Sequence - 线段树区间取最值 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「HDU5306」Gorgeous Sequence - 线段树区间取最值

题目大意

    维护一个数组$a$,支持以下操作:

  • 给出$l,r,x$,对$i\in[l,r]$,将$a_i$变为$\min(a_i,x)$。
  • 给出$l,r$,对$i\in[l,r]$,询问$a_i$的最大值。
  • 给出$l,r$,询问$\sum_{i=l}^r a_i$。

题目分析

论文题(%%%jry)。

线段树维护最大值$max$,次大值$sec$,最大值个数$maxt$。
若使区间$[L,R]$对$x$取$\min$,先定位这个区间,对定位到的区间暴力递归:

  • 当$max\le x$,直接退出。
  • 当$sec\lt x\lt max$时,维护区间和,更新$max$,打标记,退出。
  • 当$sec\ge x$,递归左右儿子。

时间复杂度证明

原论文给出了一种正确的证明,这里用另一种方式证明。

我们只讨论含有递归的修改操作,若不递归,说明满足$sec\lt v\lt max$或$v\ge max$,这对时间复杂度没有影响。

若递归,此时必有一个$max$被修改到$le$原有$sec$,至少把一个数变成和另一个数一样,即可用值域至少减少了$1$,值域只可能减少$n$次,而在线段树上定位的时间复杂度是$O(\log n)$,故修改操作的时间复杂度是$O(n\log n)$,故总时间复杂度是$O(n\log n+q\log n)$。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
const int maxn=1000005;

struct Tree {
int left,right;
LL sum,max,sec,maxt;
Tree() {}
Tree(int l,int r,LL s,LL m,LL se,LL mt):left(l),right(r),sum(s),max(m),sec(se),maxt(mt) {}
};

struct Segment_Tree {
Tree tree[maxn*4];
#define ls index<<1
#define rs index<<1|1
void build(int index,int Left,int Right,LL* a) {
tree[index].left=Left;
tree[index].right=Right;
if(Left==Right) {
tree[index]=Tree(Left,Right,a[Left],a[Left],0,1);
return;
}
int mid=(Left+Right)>>1;
build(ls,Left,mid,a);
build(rs,mid+1,Right,a);
push_up(index);
}
void push_up(int index) {
tree[index]=Tree(tree[index].left,tree[index].right,tree[ls].sum+tree[rs].sum,0,0,0);
if(tree[ls].max==tree[rs].max) {
tree[index].max=tree[ls].max;
tree[index].maxt=tree[ls].maxt+tree[rs].maxt;
tree[index].sec=max(tree[ls].sec,tree[rs].sec);
}
if(tree[ls].max>tree[rs].max) {
tree[index].max=tree[ls].max;
tree[index].maxt=tree[ls].maxt;
tree[index].sec=max(tree[ls].sec,tree[rs].max);
}
if(tree[ls].max<tree[rs].max) {
tree[index].max=tree[rs].max;
tree[index].maxt=tree[rs].maxt;
tree[index].sec=max(tree[rs].sec,tree[ls].max);
}
}
void push_down(int index) {
if(tree[ls].max>tree[index].max) {
tree[ls].sum-=tree[ls].maxt*(tree[ls].max-tree[index].max);
tree[ls].max=tree[index].max;
}
if(tree[rs].max>tree[index].max) {
tree[rs].sum-=tree[rs].maxt*(tree[rs].max-tree[index].max);
tree[rs].max=tree[index].max;
}
}
void modify(int index,int Left,int Right,LL v) {
if(tree[index].left>Right||tree[index].right<Left)return;
if(v>=tree[index].max)return;
if(Left<=tree[index].left&&tree[index].right<=Right&&tree[index].sec<v) {
tree[index].sum-=tree[index].maxt*(tree[index].max-v);
tree[index].max=v;
return;
}
push_down(index);
modify(ls,Left,Right,v);
modify(rs,Left,Right,v);
push_up(index);
}
LL query_max(int index,int Left,int Right) {
if(tree[index].left>Right||tree[index].right<Left)return 0;
if(Left<=tree[index].left&&tree[index].right<=Right)return tree[index].max;
push_down(index);
return max(query_max(ls,Left,Right),query_max(rs,Left,Right));
}
LL query_sum(int index,int Left,int Right) {
if(tree[index].left>Right||tree[index].right<Left)return 0;
if(Left<=tree[index].left&&tree[index].right<=Right)return tree[index].sum;
push_down(index);
return query_sum(ls,Left,Right)+query_sum(rs,Left,Right);
}
} st;

int n,m;
LL a[maxn];

int main() {
int t=Get_Int();
while(t--) {
n=Get_Int();
m=Get_Int();
for(int i=1; i<=n; i++)a[i]=Get_Int();
memset(st.tree,0,sizeof(st.tree));
st.build(1,1,n,a);
for(int i=1; i<=m; i++) {
int opt=Get_Int(),x=Get_Int(),y=Get_Int();
if(opt==0)st.modify(1,x,y,Get_Int());
else if(opt==1)printf("%lld\n",st.query_max(1,x,y));
else printf("%lld\n",st.query_sum(1,x,y));
}
}
return 0;
}
姥爷们赏瓶冰阔落吧~