首先显然可以把点看成斜率,然后就是求一个从头开始的最长上升子序列,支持单点修改。
分块做法(无脑):
考虑把序列分成
B
块,每一块维护一个从头开始的最长上升子序列,并用一个栈记录。每次询问相当于把所有块扫一遍,在当前块栈中二分到第一个大于上一块最大值的位置,然后贡献加上其到栈顶的元素个数,复杂度
线段树做法(思维):
发现修改一个点只会对其后面的部分有影响。我们记
max
为当前区间最大值,
ans
为只考虑当前区间的答案,考虑如何update,就是如何合并两个子树信息。
考虑当前
ansx
应该为
anslson
,加上
ansrson
中大于
maxlson
的部分,于是我们要实现一个函数
cal(x,d)
表示
x
中起始值大于
若
lsonmax≤d
,不用考虑左子树,直接递归右子树即可。
若
lsonmax>d
,答案为
cal(lson,d)+cal(rson,maxlson)
,但后半部分显然为
ansx−anslson
,所以只要递归左子树即可。
因为每次update也是
O(logn)
的,所以总复杂度
O(nlog2n)
。
分块代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ID(x) (((x)-1)/B+1)
#define L(x) ((x-1)*B+1)
#define R(x) min(n,x*B)
#define ll long long
using namespace std;
const int B=400;
const int maxn=100010;
int n,m,h[maxn];
bool cmp(int p,int q){return (!p&&h[q])||((ll)h[p]*q<(ll)h[q]*p);}
struct block
{
int top,l,r,st[B+10];
void init(int x){l=L(x);r=R(x);}
void rebuild(){top=0;for(int i=l;i<=r;i++)if(cmp(st[top],i)) st[++top]=i;}
int qry(int &last){int x=upper_bound(st+1,st+top+1,last,cmp)-st;if(x<=top) last=st[top];return top-x+1;}
}a[ID(maxn)+10];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=ID(n);i++) a[i].init(i);
for(int i=1,x;i<=m;i++)
{
scanf("%d",&x);scanf("%d",&h[x]);a[ID(x)].rebuild();
int last=0,ans=0;for(int i=1;i<=ID(n);i++)ans+=a[i].qry(last);
printf("%d\n",ans);
}
return 0;
}
线段树代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=100010;
int n,m;
struct tree
{
tree *ls,*rs;int s;double mx;
tree(){ls=rs=NULL;}
void build(int l,int r)
{
mx=0;s=0;
if(l==r) return ;
int mid=l+r>>1;
(ls=new tree)->build(l,mid);
(rs=new tree)->build(mid+1,r);
}
int cal(int l,int r,double dw)
{
if(l==r) return mx>dw;
int mid=l+r>>1;
if(ls->mx<=dw) return rs->cal(mid+1,r,dw);
else return s-ls->s+ls->cal(l,mid,dw);
}
void mdf(int l,int r,int pl,double d)
{
if(l==r){mx=d;s=1;return;}
int mid=l+r>>1;
if(pl<=mid) ls->mdf(l,mid,pl,d);
else rs->mdf(mid+1,r,pl,d);
mx=max(ls->mx,rs->mx);
s=ls->s+rs->cal(mid+1,r,ls->mx);
}
}*xtr;
int main()
{
scanf("%d%d",&n,&m);
(xtr=new tree)->build(1,n);
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
xtr->mdf(1,n,x,(double)y/x);
printf("%d\n",xtr->s);
}
return 0;
}