P4198 楼房重建
Posted bztminamoto
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4198 楼房重建相关的知识,希望对你有一定的参考价值。
很妙的思路
首先,我们可以把每一栋楼房转化为它的顶部到原点这条直线的斜率,这样就变成了从一个序列中选出一个最长上升子序列(其实不是最长上升子序列,不过可以这么理解)
考虑用线段树来维护,对于每个区间,我们维护这个区间的最大值以及这个区间的答案,那么最后的答案就是(ans[1])
对于叶节点来说,最大值就是它自己,答案为(1)
考虑怎么合并区间。
首先左边区间能看到的答案,当前区间必然也能看到
考虑右边的区间。如果右区间的最大值小于等于左区间的最大值,那么一定会被挡住啥都看不见
如果右区间的左区间的最大值小于等于左区间的最大值,左区间肯定会被挡住啥都看不见,那么我们就递归进右区间的右区间继续找答案
如果右区间的左区间的最大值大于左区间的最大值,那么所有右区间的右区间里所有原来能看到的现在还是能看到。而左区间里继续递归找
有点绕,建议看代码理解比较好
总的复杂度为(O(nlog^2n))
//minamoto
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
int read(){
int res,f=1;char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(int x){
if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
while(z[++Z]=x%10+48,x/=10);
while(sr[++C]=z[Z],--Z);sr[++C]='
';
}
const int N=1e5+5;
int n,m,ans[N<<2];double mx[N<<2];
#define ls (p<<1)
#define rs (p<<1|1)
int query(int p,int l,int r,double sl){
if(mx[p]<=sl)return 0;if(l==r)return mx[p]>sl;
int mid=(l+r)>>1;
if(mx[ls]<=sl)return query(rs,mid+1,r,sl);
return query(ls,l,mid,sl)+ans[p]-ans[ls];
}
void upd(int p,int l,int r,int x,double sl){
if(l==r)return (void)(ans[p]=1,mx[p]=sl);
int mid=(l+r)>>1;
x<=mid?upd(ls,l,mid,x,sl):upd(rs,mid+1,r,x,sl);
mx[p]=max(mx[ls],mx[rs]);
ans[p]=ans[ls]+query(rs,mid+1,r,mx[ls]);
}
int main(){
// freopen("testdata.in","r",stdin);
n=read(),m=read();
while(m--){
int x=read(),y=read();
upd(1,1,n,x,1.0*y/x);
print(ans[1]);
}
return Ot(),0;
}
以上是关于P4198 楼房重建的主要内容,如果未能解决你的问题,请参考以下文章