前置技能:树状数组
切分,相信大家都会写的。
题解部分:
题目就是让我们求能力值大于等于B的人构成的联通块的个数。
假设一开始有一片平原,然后在平原上一个位置对应一个人。对于每一次询问,如果一个人的能力值满足要求,他就会形成一座山,如果不行就还是平原。
假设一次询问后,这片地方是这样的:(假设询问的B为4)
我们可以发现,联通块的数量就是沟壑的数量除以2,而分析每一个沟壑,我们可以知道它出现的时间。假设一个沟壑对应两边能力值较小的人的能力值为X,对应两边能力值较大的人的能力值为Y,那么如果询问的B在X+1到Y这个范围内时,这个沟壑就会存在。我们可以用树状数组来维护这个东西。每次修改在树状数组里修改就是了。
注意我们要计算最左边和最右边的沟壑(可以理解为就是第1个人与第0个人之间的沟壑,同理的还有第n个人与第n+1个人之间的沟壑)。
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 200005
#define lowbit(x) x&-x
using namespace std;
struct Que{
int op,x,y;
bool operator <(const Que &_)const{
return x<_.x;
}
}Q[M];
int n,m;
int val[M];
int B[M<<2],sz;
int Get_num(int x){return lower_bound(B+1,B+sz+1,x)-B;}
void Init(){
for(int i=1;i<=n;i++)B[++sz]=val[i];
for(int i=1;i<=m;i++)if(Q[i].op==2)B[++sz]=Q[i].y;
else B[++sz]=Q[i].x;
sort(B+1,B+sz+1);
sz=unique(B+1,B+sz+1)-B-1;
for(int i=1;i<=n;i++)val[i]=Get_num(val[i]);
for(int i=1;i<=m;i++){
if(Q[i].op==2)Q[i].y=Get_num(Q[i].y);
else Q[i].x=Get_num(Q[i].x);
}
}
struct Bin{
int num[M<<2];
void clear(){
memset(num,0,sizeof(num));
}
void Add(int x,int d){
x++;//x可能等于0
while(x<(M*3)){
num[x]+=d;
x+=lowbit(x);
}
}
int sum(int x){
x++;
int res=0;
while(x){
res+=num[x];
x-=lowbit(x);
}
return res;
}
}BT;
void Updata(int i,int op){
int L=val[i-1],R=val[i];
if(L>R)swap(L,R);
L++;
BT.Add(L,op);BT.Add(R+1,-op);
L=val[i],R=val[i+1];
if(L>R)swap(L,R);
L++;
BT.Add(L,op);BT.Add(R+1,-op);
}
void Solve(){
Init();//离散化
BT.clear();
for(int i=1;i<=n+1;i++){//沟壑(包含最左边和最右边的) 这里假设第0个人与第n+1个人的能力值为0
int L=val[i-1],R=val[i];
if(L>R)swap(L,R);
L++;
BT.Add(L,1);BT.Add(R+1,-1);
}
for(int i=1;i<=m;i++){
if(Q[i].op==1){
int ans=BT.sum(Q[i].x)/2;//答案数量为沟壑数量/2
printf("%d\n",ans);
}else {
Updata(Q[i].x,-1);//在树状数组上更新
val[Q[i].x]=Q[i].y;
Updata(Q[i].x,1);
}
}
}
int main(){
freopen("plan.in","r",stdin);
freopen("plan.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int i=1;i<=m;i++){
scanf("%d",&Q[i].op);
if(Q[i].op==1)scanf("%d",&Q[i].x);
else scanf("%d%d",&Q[i].x,&Q[i].y);
}
Solve();
return 0;
}