P4198 樓房重建 (線段樹維護區間上升子序列)
阿新 • • 發佈:2019-02-07
題目描述
小A的樓房外有一大片施工工地,工地上有N棟待建的樓房。每天,這片工地上的房子拆了又建、建了又拆。他經常無聊地看著窗外發呆,數自己能夠看到多少棟房子。
為了簡化問題,我們考慮這些事件發生在一個二維平面上。小A在平面上(0,0)點的位置,第i棟樓房可以用一條連線(i,0)和(i,Hi)的線段表示,其中Hi為第i棟樓房的高度。如果這棟樓房上任何一個高度大於0的點與(0,0)的連線沒有與之前的線段相交,那麼這棟樓房就被認為是可見的。
施工隊的建造總共進行了M天。初始時,所有樓房都還沒有開始建造,它們的高度均為0。在第i天,建築隊將會將橫座標為Xi的房屋的高度變為Yi(高度可以比原來大—修建,也可以比原來小—拆除,甚至可以保持不變—建築隊這天什麼事也沒做)。請你幫小A數數每天在建築隊完工之後,他能看到多少棟樓房?
輸入輸出格式
輸入格式:
第一行兩個正整數N,M
接下來M行,每行兩個正整數Xi,Yi
輸出格式:
M行,第i行一個整數表示第i天過後小A能看到的樓房有多少棟
輸入輸出樣例
輸入樣例#1: 複製
3 4 2 4 3 6 1 1000000000 1 1
輸出樣例#1: 複製
1 1 1 2
說明
對於所有的資料1<=Xi<=N,1<=Yi<=10^9
N,M<=100000
#include<bits/stdc++.h> using namespace std; #define LL long long #define N 100005 double M[N<<2]; int cnt[N<<2]; int cal(int rt,int l,int r,double h) { if(l==r)return M[rt]>h?1:0; if(M[rt<<1]<=h)return cal(rt<<1|1,(l+r)/2+1,r,h); else return cal(rt<<1,l,(l+r)>>1,h)+cnt[rt]-cnt[rt<<1]; } void update(int rt,int l,int r,int pos,double h) { if(l==r) { M[rt]=h; cnt[rt]=1; return ; } int m=(l+r)>>1; if(pos<=m) update(rt<<1,l,m,pos,h); else update(rt<<1|1,m+1,r,pos,h); M[rt]=max(M[rt<<1],M[rt<<1|1]); cnt[rt]=cnt[rt<<1]+cal(rt<<1|1,m+1,r,M[rt<<1]); } int main() { int n,m; scanf("%d%d",&n,&m); while(m--) { int l,h; scanf("%d%d",&l,&h); update(1,1,n,l,(double)h/l); printf("%d\n",cnt[1]); } }