本文版權歸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 }