题目
给一个序列,支持单点修改,每次修改后输出对这个序列做单调递增栈后栈的大小
思路
线段树维护单调栈模板题
先开个线段树,设(query(rt,l,r,x))表示询问([l,r])这段区间中比(x)大的数形成的单调栈大小,(mx[rt])表示该子树中的最大值
显然有(query(rt,l,r,x) = query(ls,l,mid,x) + query(rs,mid+1,r,mx[ls]))
每次都递归到底是不行的,考虑用一个中间值存其中一个询问,由于最终询问为(query(1,1,n,0)),设(ans[rt]=query(rt,l,r,0))
修改时mx显然可以直接(O(1))求的,而(ans)用上面的递推式,只需要快速得到(query(rs,mid+1,r,mx[ls]))即可
对于询问(query(rt,l,r,x)):
-
(mx[ls]leq x),即左区间没有贡献,直接递归右区间(query(rs,mid+1,r,x))
-
(mx[ls]>x),左区间有贡献,此时递归左区间(query(ls,l,mid,x)),右区间为(query(rs,mid+1,r,mx[ls])=ans[rt]-ans[ls])
于是询问只需要递归(log)次,而修改的pushup共需要log次询问,总复杂度为(O(nlog^2n))
Code
#include<bits/stdc++.h>
#define N 100005
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef long long ll;
int n,m,ans[N<<2];
double mx[N<<2];
template <class T>
void read(T &x)
{
char c; int sign=1;
while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
int query(int rt,int l,int r,double k)//查询大于k情况下单增的单调栈长度
{
if(l==r) return (mx[rt]>k);
if(mx[rt]<=k) return 0;//全部都很小
int mid=(l+r)>>1;
if(mx[rt<<1]<=k) return query(rt<<1|1,mid+1,r,k);
return query(rt<<1,l,mid,k)+ans[rt]-ans[rt<<1];
}
void modify(int rt,int l,int r,int x,double val)
{
if(l==r) { ans[rt]=1; mx[rt]=val; return; }
int mid=(l+r)>>1;
if(x<=mid) modify(rt<<1,l,mid,x,val);
else modify(rt<<1|1,mid+1,r,x,val);
mx[rt]=Max(mx[rt<<1],mx[rt<<1|1]);
ans[rt]=ans[rt<<1]+query(rt<<1|1,mid+1,r,mx[rt<<1]);
}
int main()
{
read(n);read(m);
while(m--)
{
int x,y;
read(x);read(y);
modify(1,1,n,x,1.0*y/x);
printf("%d
",ans[1]);
}
return 0;
}