BZOJ2957 楼房重建
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
Description
小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。
为了简化问题,我们考虑这些事件发生在一个二维平面上。小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度。如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。
施工队的建造总共进行了M天。初始时,所有楼房都还没有开始建造,它们的高度均为0。在第i天,建筑队将会将横坐标为Xi的房屋的高度变为Yi(高度可以比原来大---修建,也可以比原来小---拆除,甚至可以保持不变---建筑队这天什么事也没做)。请你帮小A数数每天在建筑队完工之后,他能看到多少栋楼房?
Input
第一行两个正整数N,M
接下来M行,每行两个正整数Xi,Yi
Output
M行,第i行一个整数表示第i天过后小A能看到的楼房有多少栋
Sample Input
3 4
2 4
3 6
1 1000000000
1 1
Sample Output
1
1
1
2
数据约定
对于所有的数据1<=Xi<=N,1<=Yi<=10^9
N,M<=100000
HINT
Source
正解:分块 or 线段树
解题报告:
这题既可以写分块,也可以写线段树,我试着两个都写了,发现线段树重在思想,而分块大法就是好(wu)用(nao)。
线段树做法:对于线段树每个结点维护两个值:ans和maxl,ans表示只考虑这个区间的可视区间的答案,maxl表示这个区间的最大斜率。那么问题的关键就在于如何合并两个区间,显然左区间的答案肯定可以作为总区间的答案,那么接下来就是看右区间有多少个在新加入左区间的约束后是可行的。考虑如果右区间最大值都小于等于左区间最大值那么右区间就没有贡献了,相当于是被整个挡住了。
如果大于最大值,就再考虑右区间的两个子区间:左子区间、右子区间,加入左子区间的最大值小于等于左区间最大值,那么就递归处理右子区间;否则就递归处理左子区间,然后加上右子区间原本的答案。考虑这样做的必然性:因为加入左区间最高的比左子区间最高的矮,那么相当于是左区间对于右子区间没有约束,都是左子区间产生的约束。但是右子区间的答案要用右区间答案-左子区间答案,不能直接调用右子区间本身答案,因为其本身答案没有考虑左子区间的约束。
分块做法:对于每个块内维护一个最长上升子序列,但是不同于一般的最长上升子序列,想想就可以想通。每次修改就暴力修改整个块就可以了。
线段树:
1 //It is made by ljh2000 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 using namespace std; 14 typedef long long LL; 15 #define RG register 16 const int MAXN = 100011; 17 int n,m,ans,pos; 18 double val; 19 struct node{ 20 double maxl; 21 int cnt; 22 }a[MAXN*4]; 23 24 inline int getint() 25 { 26 RG int w=0,q=0; RG char c=getchar(); 27 while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 28 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w; 29 } 30 31 inline void build(int root,int l,int r){ 32 if(l==r) return ; int mid=(l+r)>>1; int lc=root*2,rc=lc+1; 33 build(lc,l,mid); build(rc,mid+1,r); 34 } 35 36 inline int calc(int root,int l,int r,double height){ 37 if(l==r) return a[root].maxl>height; 38 int mid=(l+r)>>1; int lc=root*2,rc=lc+1; 39 if(a[lc].maxl<=height) return calc(rc,mid+1,r,height); 40 return calc(lc,l,mid,height)+a[root].cnt-a[lc].cnt; 41 } 42 43 inline void update(int root,int l,int r){ 44 if(l==r) { a[root].maxl=val; a[root].cnt=1; return ; } 45 int mid=(l+r)>>1; int lc=root*2,rc=lc+1; if(pos<=mid) update(lc,l,mid); else update(rc,mid+1,r); 46 a[root].maxl=max(a[lc].maxl,a[rc].maxl); 47 a[root].cnt=a[lc].cnt+calc(rc,mid+1,r,a[lc].maxl); 48 } 49 50 inline void work(){ 51 n=getint(); m=getint(); int x,y; build(1,1,n); 52 while(m--) { 53 x=getint(); y=getint(); pos=x; val=(double)y/x; 54 update(1,1,n); 55 printf("%d\n",a[1].cnt); 56 } 57 } 58 59 int main() 60 { 61 work(); 62 return 0; 63 }
分块:
1 //It is made by ljh2000 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 using namespace std; 14 typedef long long LL; 15 #define RG register 16 const int MAXN = 100011; 17 const int SIZE = 1000; 18 int n,m,blo,kcnt,ans,belong[MAXN]; 19 double val,b[MAXN]; 20 struct block{ 21 int l,r,cnt; 22 double s[SIZE]; 23 }a[SIZE]; 24 25 inline int getint() 26 { 27 RG int w=0,q=0; RG char c=getchar(); 28 while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 29 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w; 30 } 31 32 inline void build(){ 33 blo=550;/*blo=sqrt(n+1);*/ kcnt=n/blo; if(n%blo) kcnt++; for(int i=1;i<=n;i++) belong[i]=(i-1)/blo+1,b[i]=-1; 34 for(int i=1;i<=kcnt;i++) a[i].l=(i-1)*blo+1,a[i].r=min(i*blo,n); 35 } 36 37 inline void solve(int x){//维护最长上升子序列 38 b[x]=val; int from=belong[x]; a[from].cnt=0; double last=0; 39 for(int i=a[from].l;i<=a[from].r;i++) if(b[i]>last) { a[from].cnt++; a[from].s[a[from].cnt]=b[i]; last=b[i];} 40 last=a[1].s[a[1].cnt]; ans=a[1].cnt; int l,r,mid,wei; 41 for(int i=2;i<=kcnt;i++) { 42 l=1; r=a[i].cnt; wei=-1; 43 while(l<=r) { 44 mid=(l+r)>>1; 45 if(a[i].s[mid]>last) r=mid-1,wei=mid; 46 else l=mid+1; 47 } 48 if(wei==-1) continue; 49 last=a[i].s[a[i].cnt]; ans+=a[i].cnt-wei+1; 50 } 51 printf("%d\n",ans); 52 } 53 54 inline void work(){ 55 n=getint(); m=getint(); int x,y; build(); 56 while(m--) { 57 x=getint(); y=getint(); val=(double)y/x; 58 solve(x); 59 } 60 } 61 62 int main() 63 { 64 work(); 65 return 0; 66 }
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!