BZOJ 2957: 楼房重建
Description
求一个最长上升子序列,支持修改。\(n,m\leqslant 10^5\)
Solution
线段树。
线段树维护区间最大值和区间最长上升子序列长度。
主要的就是一个\(getw(x,v)\),表示\(x\)节点从\(v\)进入后最长上升子序列长度。
两种情况
一种是\(v\geqslant max[lc]\),这时候左子树没贡献,递归右子树。
一种是\(v<max[lc]\),这时候右子树的贡献已经统计过了,就是当前节点最长上升子序列-左节点最长上升子序列,然后递归左子树即可。
复杂度\(O(nlog^2n)\)
Code
/************************************************************** Problem: 2957 User: BeiYu Language: C++ Result: Accepted Time:1880 ms Memory:5984 kb ****************************************************************/ #include <bits/stdc++.h> using namespace std; const int N = 100050; inline int in(int x=0,char ch=getchar()) { while(ch>'9' || ch<'0') ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x; } int n,m; struct Seg { #define lc (o<<1) #define rc (o<<1|1) #define mid ((l+r)>>1) double mx[N<<2]; int d[N<<2]; int Getw(int o,int l,int r,double v) { if(l==r) return v<mx[o]; if(mx[lc]<=v) return Getw(rc,mid+1,r,v); else return Getw(lc,l,mid,v)+d[o]-d[lc]; } void Update(int o,int l,int r) { mx[o]=max(mx[lc],mx[rc]); d[o]=d[lc]+Getw(rc,mid+1,r,mx[lc]); } void Build(int o,int l,int r) { if(l==r) { mx[o]=0,d[o]=1;return; } Build(lc,l,mid),Build(rc,mid+1,r); Update(o,l,r); } void Modify(int o,int l,int r,int x,double v) { if(l==r) { mx[o]=v,d[o]=1;return; } if(x<=mid) Modify(lc,l,mid,x,v); else Modify(rc,mid+1,r,x,v); Update(o,l,r); } }py; int main() { n=in(),m=in(); py.Build(1,1,n); for(int x,y;m--;) { x=in(),y=in(); py.Modify(1,1,n,x,(double)y/x); printf("%d\n",py.Getw(1,1,n,0)); } return 0; }