1. 程式人生 > >考古研究——四十五度翻轉+時光倒流+線段樹二分

考古研究——四十五度翻轉+時光倒流+線段樹二分

image 火山噴發 問題 ali int 進行 作用 模擬 線上

3.考古研究

(geologic.pas/c/cpp)

【問題描述】

很久很久以前,有一個叫NOIP的高級文明十分繁榮。但是由於火山噴發,這個高級文明最終還是毀滅了。NOIP文明沿著直線狀的河發展,當NOIP文明毀滅的時候,這塊地表變成了平地。NOIP文明的遺跡可以看作坐標平面的x軸。y軸為高度。也就是說,在坐標平面上,直線y=0為地表,y>0的區域為地面上方,y<0的區域為地面下方。此外,從NOIP文明毀滅的時候算起,a年前(a>=0)的地層在直線y=-a的位置上。

NOIP文明毀滅後,NOIP文明的遺跡上發生了Q次地殼變動。第i次(1<=i<=Q)地殼變動用位置Xi

,方向Di和變動量Li表示。Di為1或者2。第i次地殼變動以下文所述方式進行:

  • 地層的移動方式如下:

○ Di=1時,沿著經過點(Xi, 0),斜率為1的直線形成斷層,在這條直線上方的地層會沿著直線向上移動Li。也就是說,這條直線上方的點(x,y)會移動到(x+Li, y+Li)。

○ Di=2時,沿著經過點(Xi, 0),斜率為-1的直線形成斷層,在這條直線上方的地層會沿著直線向上移動Li。也就是說,這條直線上方的點(x,y)會移動到(x-Li, y+Li)。

  • 此後,y>0區域的地層會風化作用全部消失。

時光輪轉,回到現代。考古學家LC博士開始發掘NOIP文明的遺跡。LC博士希望知道哪個位置的地表的地層是NOIP文明毀滅前多少年前的地層。目前已經知道發生過哪些地殼變動。你的工作是幫助LC博士計算,對於1<=i<=N的各個整數i,點(i-1, 0)和點(i, 0)之間的地表的地層是NOIP文明毀滅前多少年前的地層。

請寫一個程序,給定NOIP文明的遺跡上發生的地殼變動的信息,對於所有整數i(1<=i<=N),輸出點(i-1, 0)和點(i, 0)之間的地表底層是NOIP文明毀滅前多少年的地層。

【輸入】

第一行是兩個用空格隔開的整數N和Q。N是要計算的地點的個數,Q是地殼變動發生的回數。

接下來Q行中第i行(1<=i<=Q)包含3個空格隔開的整數Xi, Di, Li。分別表示第i次地殼變動的位置,方向和變動量。

輸出】

輸出一共N行。第i行(1<=i<=N)為一個整數,表示點(i-1, 0)和點(i, 0)之間的地表底層是NOIP文明毀滅前多少年的地層。

技術分享圖片

【數據範圍】

30%的數據滿足:N<=100,Q<=100,|Xi|<=100,Li=1

70%的數據滿足:N<=3 000,Q<=3 000

100%的數據滿足:1 ≦ N ≦ 200 0001 ≦ Q ≦ 200 000 |Xi |≦ 1 000 000 000

1 ≦ Di ≦ 2 1 ≦ Li ≦ 1 000 000 000 (1 ≦ i ≦ Q)

題目特別神!!

真的特別神!!

30pts可以直接模擬。把地形拆成若幹小塊。

發現,風幹的話,就把我們地表出保留的信息全部刪除了。太浪費。

由於最後只要知道地表的信息。

我們時光倒流。

把上升改為沈降。

開始是一條長度為N的線段。

隨著沈降會分成若幹段。

每一個點的深度,就是最後地表這個位置的層號。

這個怎麽維護呢?

四十五度實在不行。

考慮翻轉坐標系。

技術分享圖片

其實 就是,原來的(x,y)->(x-y,x+y)=(x‘,y‘)

那麽,現在,斜率為一的操作,就是豎劈一刀。

斜率為-1的操作,就是橫劈一刀。

①操作1

新的縱坐標y‘>xi的點都會往右走2*L

橫坐標+2*L

縱坐標不變。

②操作2

新的橫坐標x‘<=xi的點都會往下走2*L

縱坐標-2*L

橫坐標不變。

發現,無論怎麽操作,由於是一個後綴前綴的移動,每個點的橫坐標、縱坐標的相對大小都不變。

所以像是一個區間!!

可以用兩棵線段樹。一棵維護橫坐標,一棵維護縱坐標。

後綴前綴,就要找到第一個>xi的位置,第一個<=xi的位置。

直接二分即可。

最後,兩棵線段樹dfs一遍。

要還原真實的橫坐標。y=(y‘-x‘)/2

然後,y=-y才是真正的層數(越往下號越大)

代碼:

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
const int N=200000+5;
const int inf=0x3f3f3f3f;
int n,m;
struct node{
    int xi,di,L;
}q[N];
struct tree{
    ll mx,ad;
    #define tr t[c]
}t[3][4*N];
void pushup(int c,int x){
    tr[x].mx=max(tr[x<<1].mx,tr[x<<1|1].mx);
}
ll p[3][N];
void build(int c,int x,int l,int r){
    if(l==r){
        tr[x].mx=l;
        tr[x].ad=0;
        return;
    }
    build(c,x<<1,l,mid);
    build(c,x<<1|1,mid+1,r);
    pushup(c,x);
}
void pushdown(int c,int x){
    if(!tr[x].ad) return;
    tr[x<<1].ad+=tr[x].ad;
    tr[x<<1].mx+=tr[x].ad;
    tr[x<<1|1].ad+=tr[x].ad;
    tr[x<<1|1].mx+=tr[x].ad;
    tr[x].ad=0;
} 
int pos;
void find1(int x,int l,int r,int k){//t[1]
    if(l==r){
        if(t[1][x].mx<=k) pos=max(pos,l);
        return;
    }
    pushdown(1,x);
    if(t[1][x<<1].mx<=k) {
        pos=max(pos,mid);find1(x<<1|1,mid+1,r,k);
    }
    else {
        find1(x<<1,l,mid,k);
    }
}
void find2(int x,int l,int r,int k){//t[2]
    if(l==r){
        if(t[2][x].mx>k) pos=min(pos,l);
        return;
    }
    pushdown(2,x);
    if(t[2][x<<1].mx<=k){
        find2(x<<1|1,mid+1,r,k);
    }
    else {
        find2(x<<1,l,mid,k);
    }
}
void add(int c,int x,int l,int r,int L,int R,int val){
    if(L<=l&&r<=R){
        tr[x].mx+=val;
        tr[x].ad+=val;
        return;
    }
    pushdown(c,x);
    if(L<=mid) add(c,x<<1,l,mid,L,R,val);
    if(mid<R) add(c,x<<1|1,mid+1,r,L,R,val);
    pushup(c,x);
}
void dfs(int c,int x,int l,int r){
    if(l==r){
        p[c][l]=tr[x].mx;
        return;
    }
    pushdown(c,x);
    dfs(c,x<<1,l,mid);
    dfs(c,x<<1|1,mid+1,r);
}
int main(){
    scanf("%d%d",&n,&m);
    //int pos,dir,len;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&q[i].xi,&q[i].di,&q[i].L);
    }
    build(1,1,1,n);
    build(2,1,1,n);
    pos=0;//warning!! ! ! ! ! ! ! ! 
    for(int i=m;i>=1;i--){
        if(q[i].di==1){
            pos=0;
            find1(1,1,n,q[i].xi);
            if(pos)add(2,1,1,n,1,pos,-2*q[i].L);
        }
        else{
            pos=inf;
            find2(1,1,n,q[i].xi);
            if(pos!=inf)add(1,1,1,n,pos,n,2*q[i].L);
        }
    }
    dfs(1,1,1,n);
    dfs(2,1,1,n);
    for(int i=1;i<=n;i++){
        ll ans=-(p[2][i]-p[1][i])/2;
        printf("%lld\n",ans);
    }
    return 0;
}

總結:

這個題真的是神題。

放進省選組也可以。

本身題目很抽象,傾斜直線很難處理。所以傾斜坐標系,變成橫平豎直的直線。

本質上是(x,y)->(x-y,x+y)最後還原真實坐標即可。

這個技巧要註意!!

然後,由於向上升很麻煩,而且會損失很多存儲的信息。我們就讓它下沈。

而最後的位置恰好就是層數。

時光倒流。

線段樹還是比較自然的。

考古研究——四十五度翻轉+時光倒流+線段樹二分