雨中人

题意

有一个长度为 \(n\) 的整数序列 \(a_i\) 。你有 \(m\) 个操作,包括修改和询问。

对于每个询问,给出一个值 \(c\) (整数),询问序列中大于等于 \(c\) 的数字有多少个连续段。

对于每个修改,将位置 \(x\) 的数字 \(a_x\) 修改为 \(y\)

思路

考虑性质分析。如果存在两个位置使得后面比前面高,设前面的高 \(x\),后面的高 \(y\),那么水位在 \([x+1,y]\) 范围内时答案会增加 \(1\)img
如图,这是样例的模拟。对于每个上升段,如果高度没有超过前一个,那么这两块肯定会连着,那么答案肯定不会更新,当且仅当高度在 \([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;
}
posted @ 2023-03-25 21:23  wscqwq  阅读(14)  评论(0编辑  收藏  举报