线段树——讲课用——最长上升子序列
题目链接:https://www.luogu.org/problemnew/show/P4198
题解:
能用线段树解的关键:从第一个位置开始的LIS
所以说左子区间一定有效,只考虑如何将右子区间与左子区间衔接
线段树维护从节点区间左端点开始的LIS
3种情况:
1、右子区间全都能衔接——右子区间的LIS最小值(第一个位置)> 左子区间LIS最大值
2、右子区间全都不能衔接——右子区间LIS最大值 <= 左子区间LIS最大值
3、右子区间后半部分可以衔接——右子区间某一后半部分LIS最小值 > 左子区间LIS最大值
所以线段树里还要我维护对应的LIS最小值和最大值
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define N 100000 int len[N<<2|1]; double mx[N<<2|1],mi[N<<2|1]; int n,m; int ans; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } void query(int k,int l,int r,double lim) { if(mi[k]>lim) { ans+=len[k]; return; } int mid=l+r>>1; if(mx[k<<1]>lim) { query(k<<1,l,mid,lim); query(k<<1|1,mid+1,r,mx[k<<1]); } else if(mx[k<<1|1]>lim) query(k<<1|1,mid+1,r,lim); } void up(int k) { int L=k<<1,R=k<<1|1; if(mi[R]>mx[L]) { len[k]=len[L]+len[R]; mx[k]=mx[R]; if(mi[L]) mi[k]=mi[L]; else mi[k]=mi[R]; } else if(mx[R]<=mx[L]) { len[k]=len[L]; mx[k]=mx[L]; mi[k]=mi[L]; } else { ans=len[L]; query(R,1,n,mx[L]); len[k]=ans; mi[k]=mi[L]; mx[k]=mx[R]; } } void change(int k,int l,int r,int x,int y) { if(l==r) { len[k]=1; mx[k]=mi[k]=y*1.0/x; return; } int mid=l+r>>1; if(x<=mid) change(k<<1,l,mid,x,y); else change(k<<1|1,mid+1,r,x,y); up(k); } void init() { read(n); read(m); int x,y; while(m--) { read(x); read(y); change(1,1,n,x,y); printf("%d\n",len[1]); } } int main() { init(); return 0; }