要求两个操作
- 将某点a[x]改成u,即单点修改
- 求区间 [ L , R ] [L,R] [L,R]的不降子数组个数
分析
这题没有涉及区间修改,因此我们不需要push_down
;
我们假设子数组的个数为sub
很容易想到
tr[p].sub = tr[lc].sub + tr[rc].sub
那么除了这些还有别的吗?
如果当前节点的左子树 可以一直不降到 右子树
假设左子树右边的不降长度为tr[lc].rlen
,右子树左边的不降长度为tr[rc].llen
那么我们每次从左子树取长度为一接到右子树的不降长度上,根节点的sub
就会多一个;
以此类推,那么一共会多tr[lc].rlen * tr[rc].llen
个
因此sub
的维护如下
ret.sub_num = left.sub_num + right.sub_num;
if(a[left.r] <= a[right.l]){
//连续升
ret.sub_num += 1ll*left.rlen * right.llen;
}
对于根节点的llen
来说,如果其左子树能直接不降到右子树,那么有
if(left.r-left.l+1 == left.llen && a[left.r] <= a[right.l]){
ret.llen = left.llen + right.llen;
}
否则直接取左子树的llen
,则
else{
ret.llen = left.llen;
}
根节点的rlen
同理可得
区间查询注意点
对于区间查询的值,需要注意其子区间的合并是否可以直接加,不行的话需要像push_up一样合并
比如这题就不行,因为合并后的sub
可能会更大,不能直接加;
比如246. 区间最大公约数也是一个道理
AC_Code
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
#define lc (p<<1)
#define rc (p<<1|1)
typedef long long ll;
const int N = 2e5+10;
int a[N],n,q;
struct Node{
int l,r;
ll sub_num;
//左边的最长不降、右边的最长不降子数组长度
int llen,rlen;
}tr[N<<2];
Node node_merge(Node left,Node right){
Node ret;
ret.l = left.l,ret.r = right.r;
ret.sub_num = left.sub_num + right.sub_num;
if(a[left.r] <= a[right.l]){
//连续升
ret.sub_num += 1ll*left.rlen * right.llen;
}
if(left.r-left.l+1 == left.llen && a[left.r] <= a[right.l]){
ret.llen = left.llen + right.llen;
}else{
ret.llen = left.llen;
}
if(right.r-right.l+1 == right.rlen && a[left.r] <= a[right.l]){
ret.rlen = left.rlen + right.rlen;
}else{
ret.rlen = right.rlen;
}
return ret;
}
void push_up(int p){
tr[p] = node_merge(tr[lc],tr[rc]);
}
void build(int p,int l,int r){
tr[p].l = l,tr[p].r = r;
if(l == r){
tr[p].sub_num = tr[p].llen = tr[p].rlen = 1;
return;
}
int mid = (l+r) >> 1;
build(lc,l,mid);
build(rc,mid+1,r);
push_up(p);
}
//将a[x]改成u
void node_update(int p,int x,int u){
if(tr[p].l == tr[p].r){
a[x] = u;
return;
}
int mid = (tr[p].l + tr[p].r) >> 1;
if(x>mid){
node_update(rc,x,u);
}else{
node_update(lc,x,u);
}
push_up(p);
}
Node query_sub(int p,int l,int r){
if(tr[p].l>=l&&tr[p].r<=r){
return tr[p];
}
int mid = (tr[p].l+tr[p].r) >> 1;
if(r<=mid){
return query_sub(lc,l,r);
}else if(l>mid){
return query_sub(rc,l,r);
}else{
return node_merge(
query_sub(lc,l,mid),query_sub(rc,mid+1,r)
);
}
}
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >> n >> q;
for(int i=1;i<=n;++i) cin >> a[i];
build(1,1,n);
int op,x,y;
while(q--){
cin >> op >> x >> y;
if(op == 1){
node_update(1,x,y);
}else{
cout << query_sub(1,x,y).sub_num << '\n';
}
}
return 0;
}