【线段树】[Luogu P4198]楼房修建
显然要维护斜率区间单调递增
并且第一个必选,后一个比前一个选中的斜率大的必选
考虑如何合并两个区间
我们维护一个least值,least这个值必选,且之后选的都必须严格大于least,Push_Up的时候就像在线段树上二分一样做就好了
这样每次Push_Up是$logn$的,线段树单点修改时$logn$的,所以总复杂度是$O(nlog^2n)$的,再维护一个区间最大值可以做到一些不必要但是可以卡常的剪枝...
1 #include<bits/stdc++.h> 2 #define writeln(x) write(x),puts("") 3 #define writep(x) write(x),putchar(' ') 4 using namespace std; 5 inline int read(){ 6 int ans=0,f=1;char chr=getchar(); 7 while(!isdigit(chr)){if(chr=='-') f=-1;chr=getchar();} 8 while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();} 9 return ans*f; 10 }void write(int x){ 11 if(x<0) putchar('-'),x=-x; 12 if(x>9) write(x/10); 13 putchar(x%10+'0'); 14 }const int M = 2e5+5; 15 int s[M<<2],n,T; 16 double mx[M<<2],a[M]; 17 #define ls (i<<1) 18 #define rs (i<<1|1) 19 #define mid (l+r>>1) 20 inline int Push(int i,int l,int r,double least){ 21 if(mx[i]<=least) return 0; 22 if(a[l]>least) return s[i]; 23 if(l==r) return a[l]>least; 24 if(mx[ls]<=least) return Push(rs,mid+1,r,least); 25 return Push(ls,l,mid,least)+s[i]-s[ls]; 26 }inline void Push_Up(int i,int l,int r){ 27 mx[i]=max(mx[ls],mx[rs]); 28 s[i]=s[ls]+Push(rs,mid+1,r,mx[ls]); 29 }void Update(int i,int l,int r,int pos,double x){ 30 if(l==r)return mx[i]=x,s[i]=1,void(); 31 if(pos<=mid) Update(ls,l,mid,pos,x); 32 else Update(rs,mid+1,r,pos,x); 33 Push_Up(i,l,r); 34 }int main(){ 35 n=read(),T=read(); 36 while(T--){ 37 int x=read(),y=read(); 38 a[x]=y*1.0/(x*1.0); 39 Update(1,1,n,x,y*1.0/(1.0*x)); 40 printf("%d\n",s[1]); 41 }return 0; 42 }