雨中人
题意
有一个长度为 \(n\) 的整数序列 \(a_i\) 。你有 \(m\) 个操作,包括修改和询问。
对于每个询问,给出一个值 \(c\) (整数),询问序列中大于等于 \(c\) 的数字有多少个连续段。
对于每个修改,将位置 \(x\) 的数字 \(a_x\) 修改为 \(y\)
思路
考虑性质分析。如果存在两个位置使得后面比前面高,设前面的高 \(x\),后面的高 \(y\),那么水位在 \([x+1,y]\) 范围内时答案会增加 \(1\)。
如图,这是样例的模拟。对于每个上升段,如果高度没有超过前一个,那么这两块肯定会连着,那么答案肯定不会更新,当且仅当高度在 \([x+1,y]\),前者被淹没,这样后者就跟前面断了,陆地数增加,但是如果高度 \(>y\),那么前后都被淹没,也不能更新,所以只需要将 \([x+1,y]\) 的答案增加 \(1\) 即可。下降其实是不用管的,因为后面的上升会管,对于修改,就将 \(4\) 对前后的修改去掉,插入 \(1\) 的修改即可。
#include<bits/stdc++.h>
using namespace std;
#define L(i,l,r) for(int i=l;i<=r;++i)
#define R(i,l,r) for(int i=r;i>=l;--i)
const int N=200010;
int n,m,a[N],lsh[N+N],val[N],id[N],len,c[N+N];
void add(int x,int v){
for(;x<=len;x+=x&-x)c[x]+=v;
}
int sum(int x){
int res=0;
for(;x;x-=x&-x)res+=c[x];
return res;
}
void update(int l,int r,int v){
add(l,v),add(r+1,-v);
}
void updata(int pos,int value,bool flag,int v){
if(a[pos-1]<a[pos])update(a[pos-1]+1,a[pos],v);
if(flag&&a[pos]<a[pos+1])update(a[pos]+1,a[pos+1],v);
}
int main(){
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
scanf("%d%d",&n,&m);
L(i, 1, n)scanf("%d",a+i),lsh[i]=a[i];
L(i, 1, m){
int op;
scanf("%d",&op);
if(op==1){
int c;
scanf("%d",&c);
lsh[n+i]=c;
val[i]=c;
id[i]=-1;
}
else{
int x,y;
scanf("%d%d",&x,&y);
id[i]=x,val[i]=y;
lsh[n+i]=y;
}
}
sort(lsh+1,lsh+1+n+m);
len=unique(lsh+1,lsh+1+n+m)-lsh-1;
L(i, 1, n){
a[i]=lower_bound(lsh+1,lsh+1+len,a[i])-lsh;
updata(i,a[i],0,1);
}
L(i, 1, m){
val[i]=lower_bound(lsh+1,lsh+1+len,val[i])-lsh;
if(id[i]!=-1){
updata(id[i],a[id[i]],1,-1);
a[id[i]]=val[i];
updata(id[i], a[id[i]], 1, 1);
}
else{
printf("%d\n",sum(val[i]));
}
}
return 0;
}