- 线段树上维护一个单调栈,单点修改,查询(tr[1]).
- 这个不用(pushdown),主要是难在(pushup)上。
int Update(double M,int i,int l,int r){
if(tr[i].Max<=M)return 0;
if(a[l]>M)return tr[i].len;
if(l==r)return a[l]>M;
int mid=(l+r)>>1;
if(tr[i<<1].Max<=M)return Update(M,i<<1|1,mid+1,r);
else return tr[i].len-tr[i<<1].len+Update(M,i<<1,l,mid);
}
tr[i].len=tr[i<<1].len+Update(tr[i<<1].Max,i<<1|1,mid+1,r);
因为对于一个更新来说,肯定是不能不要前半截的,也就是前面的不能选择式忽略。
所以左儿子的(len)必须加上。然后去选择加上右儿子符合要求的长度。
- 在(Update)里:
如果右儿子中的最大值小于左儿子中的最大值,那就直接返回(0)就好。因为不可能有贡献了。
如果右儿子的第一个就大于左儿子的最大值,那么右儿子的(len)值就全部满足条件了,所以直接返回(len)就好。
如果这是个叶子节点的话,也是看值和左儿子最大值的关系。
接下来设(s1)是右儿子的左儿子,(s2)是右儿子的右儿子。
如果(s1.Max<=M),那么(s1)对答案已经做不了贡献了,直接返回(s2)的贡献即可。
反之,则返回答案为从(s1)中找的符合要求的长度再加上右儿子的贡献即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+1;
int n,m;
double a[maxn];
struct E{double Max;int len;}tr[maxn<<2];
int Update(double M,int i,int l,int r){
if(tr[i].Max<=M)return 0;
if(a[l]>M)return tr[i].len;
if(l==r)return a[l]>M;
int mid=(l+r)>>1;
if(tr[i<<1].Max<=M)return Update(M,i<<1|1,mid+1,r);
else return tr[i].len-tr[i<<1].len+Update(M,i<<1,l,mid);
}
void Change(int i,int l,int r,int pos,int k){
if(l==r){
tr[i].Max=(double)k/pos;
tr[i].len=1;
return;
}int mid=(l+r)>>1;
if(pos<=mid)Change(i<<1,l,mid,pos,k);
else Change(i<<1|1,mid+1,r,pos,k);
tr[i].Max=max(tr[i<<1].Max,tr[i<<1|1].Max);
tr[i].len=tr[i<<1].len+Update(tr[i<<1].Max,i<<1|1,mid+1,r);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int k,pos;scanf("%d%d",&pos,&k);
a[pos]=(double)k/pos;
Change(1,1,n,pos,k);
printf("%d
",tr[1].len);
}
return 0;
}