1. 程式人生 > >P4198 樓房重建 (線段樹維護區間上升子序列)

P4198 樓房重建 (線段樹維護區間上升子序列)

題目描述

小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]);
    }
}