解题思路:
题意即是要动态维护斜率的最长上升子序列。
考虑线段树,对于线段树每个结点维护两个值:ans和maxl,ans表示只考虑这个区间的可视区间的答案,maxl表示这个区间的最大斜率。
那么问题的关键就在于修改后如何合并两个区间(update),显然左区间的答案肯定可以作为总区间的答案的一部分,那么接下来就是看右区间有多少个在新加入左区间的约束后(即是maxl[k<<1])是可行的。
(注意文中左区间、左子区间……的区别)
考虑如果右区间最大值都小于等于左区间最大值那么右区间就没有贡献了,相当于是被整个挡住了。如果大于最大值,就再考虑右区间的两个子区间:左子区间、右子区间。如果左子区间的最大值小于等于左区间最大值,那么就递归处理右子区间;否则就递归处理左子区间,然后加上右子区间原本的答案。考虑这样做的必然性:因为左区间最高的比左子区间最高的矮,那么相当于是左区间对于右子区间没有约束,都是左子区间产生的约束。但是右子区间的答案要用右区间答案-左子区间答案,不能直接调用右子区间本身答案,因为其本身答案没有考虑左子区间的约束。
#include<bits/stdc++.h>
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=100005;
int n,m,cnt[N<<2];
double mx[N<<2];
int calc(int k,int l,int r,double v)
{
if(mx[k]<v)return 0;
if(l==r)return mx[k]>v;
int mid=l+r>>1;
if(mx[k<<1]<=v)return calc(k<<1|1,mid+1,r,v);
else return calc(k<<1,l,mid,v)+cnt[k]-cnt[k<<1];
}
void modify(int k,int l,int r,int p,double v)
{
if(l==r)
{
mx[k]=v,cnt[k]=1;
return;
}
int mid=l+r>>1;
if(p<=mid)modify(k<<1,l,mid,p,v);
else modify(k<<1|1,mid+1,r,p,v);
mx[k]=max(mx[k<<1],mx[k<<1|1]);
cnt[k]=cnt[k<<1]+calc(k<<1|1,mid+1,r,mx[k<<1]);
}
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),m=getint();
while(m--)
{
int p=getint(),h=getint();
modify(1,1,n,p,(double)h/p);
printf("%d\n",cnt[1]);
}
return 0;
}