1. 程式人生 > >題解 洛谷P4198/BZOJ2957【樓房重建】

題解 洛谷P4198/BZOJ2957【樓房重建】

每個樓房,還有修改操作。簡單的想到用線段樹來維護資訊。

顯然線段樹只需要維護y/x即可,對於每一個樓房,能看見的條件就是前面樓房的y/x的嚴格小於當前樓房的y/x。

線段樹的區間修改不再贅述。

那麼怎麼維護可以看到的樓房數呢?

考慮線上段樹的每一個節點上用一個變數sum來表示從這個節點的左端點向右端點看時能看到多少樓房。

假設現在有一區間:1,5,8,0,7,9。維護這個區間資訊的節點編號為x

x<<1維護的區間是1,5,8,從18看可以看到三個樓房,故x<<1的sum的值為3

x<<1|1維護的區間是0,7,9,從09看也可以看到三個樓房,故x<<1|1的sum

的值也為3

難道x的sum值也為3+3=6?錯!

這顯然是不能加在一起的,x的sum的值為4。

為什麼會錯呢?應為右兒子x<<1|1的觀察視角不是從1開始的,是從0開始的。

顯然0被擋住了。

顯然,為什麼這道題是紫題,主要難在的怎麼上傳資訊。(不然就是黃牌了)

首先考慮每個線段樹節點在維護一個mxmx代表這個節點所代表的區間中所有的樓房中最高的高度。

上傳時,由於兩個兒子的sum值是已經處理好的,而左兒子的視角是跟x的視角一樣的(對於上面的例子來說都是1),可以直接上傳,即sum(x)+=sum(x<<1)

對於右兒子,考慮遞迴處理右兒子的區間,遞迴時帶一個Mx

變量表示x的左兒子的最高的樓房(右兒子再左兒子後面,原因下面解釋),遞迴的返回值是當前處理區間可以被看到的樓房數(從x的視角看)

對於現在處理的每一個區間:

  • 如果這個區間的最高的樓房的高度都<=Mx(全部被擋住了),立即return 0
  • 如果這個區間最左邊的樓房高度>Mx(可以看到),那麼return 當前區間維護的sum值。
  • 如果這個區間只代表一個樓房,直接判斷其能否被看到即可。
  • 對於剩下的情況,遞迴兒子,分兩種情況討論:
    • 如果左兒子的最高的樓房小於Mx(不能對答案有任何貢獻),直接遞迴右兒子。
    • 否則,遞迴左兒子,右兒子能看到的樓房個數為當前區間sum-左兒子sum即可,(應為下面已經更新好了,只需要直接呼叫就好了。)

Code(文中的sum用l代替):

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int N=1e5+2;
struct Node {
    double mx;
    int l;
    #define m(x) tree[x].mx
    #define l(x) tree[x].l
} tree[N<<2];
int n,m;
double val[N];
inline void pushup_max(int x) {
    m(x)=max(m(x<<1),m(x<<1|1));
}
inline int pushup_sum(double Mx,int x,int l,int r) {
    if(m(x)<Mx)return 0;
    if(val[l]>Mx)return l(x);
    if(l==r)return val[l]>Mx;
    int mid=(l+r)>>1;
    if(m(lson)<=Mx)return pushup_sum(Mx,rson,mid+1,r);
    else return pushup_sum(Mx,lson,l,mid)+l(x)-l(lson);
}
inline void change(int x,int l,int r,int pos,int value) {
    if(l==r&&l==pos) {
        m(x)=(double)value/pos;
        l(x)=1;
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)change(lson,l,mid,pos,value);
    else if(pos>mid)change(rson,mid+1,r,pos,value);
    pushup_max(x);
    l(x)=l(lson)+pushup_sum(m(lson),rson,mid+1,r);
}
int main() {
    scanf("%d%d",&n,&m);
    for(register int x,y,i=1; i<=m; ++i) {
        scanf("%d%d",&x,&y);
        val[x]=(double)y/x;
        change(1,1,n,x,y);
        printf("%d\n",l(1));
    }
    return 0;
}