陶陶摘苹果(线段树的应用)
题意就是从\(1\)到\(n\)找最长严格单调上升序列长度,要求支持修改。
主要运用线段树维护区间的严格单调上升序列长度。
这道题的难点在于一般的线段树上某个结点的信息是由两个子节点转移过来,但是这道题的信息却不能这么简单的转移过来,所以\(pushup\)函数会有点难写。
为啥子呢?
因为区间严格单调上升长度并不是一个可简单合并的东西(简单来说,比如区间和直接等于左右区间和的和,而这玩意却不能直接让俩子结点的信息相加得结果)
那么如何处理呢?
我们知道,一个结点的严格单调上升序列一定完全包括其左儿子的严格单调上升序列(自己手%一下,挺好理解的)。
那么难处理的就只是怎么把右儿子的答案更新上来了。在这类题中,我们可以用左儿子去更新右儿子。
其实右儿子要更新上来的序列长度就是比左儿子最大值还大的那部分序列长度。
那么我们可以定义\(query(rt,l,r,lim)\)表示\(rt\)这个结点,左右区间是\(l\),\(r\),这个结点中满足比\(lim\)大的严格单调上升长度。
那么右儿子的答案就可以被更新成\(query(rt*2+1,mid+1,r,Max[rt*2])\)
这里需要维护区间\(Max\),用线段树顺便搞搞就行。
然后考虑这个\(query\)值是怎么获得的。
还是用分治的思想,把大区间分为小区间来算。
考虑:
- 如果这个区间只有一个数,那么直接比较这个值与\(lim\)的大小,就知道答案了。
- 如果整个区间的最大值都不大于\(lim\),那这个区间不会对结果有贡献,直接返回0。
- 如果这个区间的左区间的最大值大于\(lim\),那么这个区间的右区间的答案一定可以全部转移过来(因为右区间的答案里的每个数都大于左区间的最大值,也大于lim),左区间再去递归处理
- 如果这个区间左区间的最大值小于等于\(lim\),那左区间不可能对答案有贡献,直接递归计算右区间即可。
码:
int query(int rt,int l,int r,int lim){
if(l==r&&Max[rt]>lim)return 1;
if(Max[rt]<=lim)return 0;
int mid=l+r>>1;
if(Max[rt<<1]>lim)return query(rt<<1,l,mid,lim)+tree[rt<<1|1];
if(Max[rt<<1]<=lim&&Max[rt<<1|1]>lim)return query(rt<<1|1,mid+1,r,lim);
}
每次\(modify\)就是简单的单点修改,记得\(pushup\)就能把答案更新对。
每次计算答案就是\(query(1,1,n,0);\)因为题目说了每个苹果高度都\(>=1\),那整个区间的大于\(0\)的严格上升序列长度就是答案。
记得每次求出答案再把原序列还原。
全码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
int n,m;
int h[maxn];
int tree[maxn],Max[maxn];
int query(int rt,int l,int r,int lim){
if(l==r&&Max[rt]>lim)return 1;
if(Max[rt]<=lim)return 0;
int mid=l+r>>1;
if(Max[rt<<1]>lim)return query(rt<<1,l,mid,lim)+tree[rt<<1|1];
if(Max[rt<<1]<=lim&&Max[rt<<1|1]>lim)return query(rt<<1|1,mid+1,r,lim);
}
void push_up(int rt,int l,int r){
int mid=l+r>>1;
Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
tree[rt<<1|1]=query(rt<<1|1,mid+1,r,Max[rt<<1]);
}
void build(int rt,int l,int r){
if(l==r){
Max[rt]=h[l];
return;
}
int mid=l+r>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
push_up(rt,l,r);
}
void modify(int rt,int l,int r,int pos,int w){
if(l==r){
Max[rt]=w;
return;
}
int mid=l+r>>1;
if(pos<=mid)modify(rt<<1,l,mid,pos,w);
else modify(rt<<1|1,mid+1,r,pos,w);
push_up(rt,l,r);
}
int main(){
freopen("taopapp.in","r",stdin);
freopen("taopapp.out","w",stdout);
n=read();m=read();
for(register int i=1;i<=n;i++)h[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++){
int pos=read(),num=read();
int tmp=h[pos];
modify(1,1,n,pos,num);
printf("%d\n",query(1,1,n,0));
modify(1,1,n,pos,tmp);
}
return 0;
}