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 楼房重建的主要内容,如果未能解决你的问题,请参考以下文章

P4198 楼房重建

[Luogu P4198]楼房重建(线段树)

P4198 楼房重建

luogu P4198 楼房重建——线段树

线段树[Luogu P4198]楼房修建

bzoj2957 楼房重建——线段树