这题其实可以用分块做的,但是为了接下来的BZOJ2770做准备,还是去学了一发线段树的解法。
首先我们要明白题目的意思:题目给出一些坐标,求当前所有坐标和原点的斜率严格上升的序列的最长长度。
查看n和m的范围,明显我们要想一种时间复杂度为O(nlogn)的算法或数据结构,要求支持插入、统计最大值。
线段树显然满足我们的要求,然后这题的难点就在于怎么用线段树求以一个数为起点的最长严格上升子序列。
我们定义find(f,k)表示以f为起点,区间为k的最长严格上升子序列的长度,设k的两个儿子为lt和rt,最长严格上升子序列为w。
我们可以分类讨论:
- lt中的最大值小于等于f,显然左子树对答案没有贡献,我们直接find(f,rt)即可
- lt中的最大值大于f,我们求出了左子树中的最长严格上升子序列,显然这个子序列是以左子树内的最大值为结尾的。这时我们没必要再去find(max_lt,rt)了,因为我们已经知道这个答案了——find(max_lt,rt)==k_w-lt_w。这个很显然啊,就是根据定义来的。
综上所述,这题就被顺利解决了。
附上AC代码:
#include <cstdio>
#include <cctype>
#include <algorithm>
#define mid ((t[k].l+t[k].r)>>1)
#define lt (k<<1)
#define rt (k<<1|1)
using namespace std;
const int N=1e5+10;
struct note{
int l,r,w;
double f;
}t[N*3];
int n,m,x,y;
inline char nc(void){
static char ch[100010],*p1=ch,*p2=ch;
return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &a){
static char c=nc();int f=1;
for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
for (a=0;isdigit(c);a=(a<<3)+(a<<1)+c-'0',c=nc());
a*=f;return;
}
inline void build(int k,int l,int r){
t[k]=(note){l,r,1,0.0};
if (l==r) return;
build(lt,l,mid),build(rt,mid+1,r);
return;
}
inline int find(double f,int k){
if (t[k].l==t[k].r) return f<t[k].f;
if (f<=t[lt].f) return find(f,lt)+t[k].w-t[lt].w;
else return find(f,rt);
}
inline void updata(int k){return (void)(t[k].f=max(t[lt].f,t[rt].f),t[k].w=t[lt].w+find(t[lt].f,rt));}
inline void change(int k,int wz,double f){
if (t[k].l==t[k].r) return (void)(t[k].f=f,t[k].w=1);
if (wz<=mid) change(lt,wz,f);
else change(rt,wz,f);
return updata(k);
}
int main(void){
read(n),read(m),build(1,1,n);
while (m--) read(x),read(y),change(1,x,1.0*y/x),printf("%d\n",find(0,1));
return 0;
}