1. 程式人生 > >BZOJ2957: 樓房重建(線段樹&LIS)

BZOJ2957: 樓房重建(線段樹&LIS)

2957: 樓房重建

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 3727  Solved: 1793
[Submit][Status][Discuss]

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

 

此題的LIS值的是,從左方的視野“可見”的哪些房子,我們維護兩個東西:

      mx:一個區間max

      res:這個區間的LIS。

現在我們考慮一個區間,假設這個區間前面的視野高度為pre,那麼,那麼這個區間的mx小於等於pre,說明貢獻為0;否則如果左區間小於等於pre,那麼只考慮右區間;否則這個區間的貢獻=左區間的貢獻+res[now]-res[now<<1|1],因為左區間使用了,那麼最大值一定會使用,所以右區間的貢獻不變,所以不用下推。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace
std; const int maxn=400010; double mx[maxn];int res[maxn]; void read(int &x){ x=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } int cal(int Now,int L,int R,double pre) { if(mx[Now]<=pre) return 0;// if(L==R) return mx[Now]>pre; int Mid=(L+R)>>1; if(mx[Now<<1]<=pre) return cal(Now<<1|1,Mid+1,R,pre); return cal(Now<<1,L,Mid,pre)+res[Now]-res[Now<<1]; //由於左邊的最大值肯定會使用,所以這種情況下,右邊的值不變。 } void update(int Now,int L,int R,int pos,double h) { if(L==R) { mx[Now]=h; res[Now]=1; return ;} int Mid=(L+R)>>1; if(pos<=Mid) update(Now<<1,L,Mid,pos,h); else update(Now<<1|1,Mid+1,R,pos,h); mx[Now]=max(mx[Now<<1],mx[Now<<1|1]); if(mx[Now<<1]>=mx[Now<<1|1]) res[Now]=res[Now<<1]; else res[Now]=res[Now<<1]+cal(Now<<1|1,Mid+1,R,mx[Now<<1]); } int main() { int N,M,p,h; scanf("%d%d",&N,&M); rep(i,1,M){ read(p); read(h); update(1,1,N,p,(double)h/p); printf("%d\n",res[1]); } return 0; }