cf 474E 線段樹 dp 離散化後二分查詢

2021-07-03 10:19:41 字數 1627 閱讀 1682

題意:給你n個柱子,每個柱子都有乙個高度,你從第乙個柱子開始跳往後跳,後面的柱子要滿足|hi-hj|>=d才可以跳上去,問最多跳多少步,並且輸出一種跳法,從小到大輸出柱子號。

思路:真肯定是個dp,而且思路很簡單//轉移方程 dp[i]=max(dp[j])+1,(|hi-hj|>=d)

但是n的範圍是10^5,那麼你每次都對之前的柱子掃一邊找最大就不行了,那樣就是n^2的演算法,所以要對每次找最大進行優化,這裡優化的方式就是應用線段樹,而輸出路徑就用遞迴就行,由於沒有怎麼輸出過路徑導致不太會。

線段樹優化的道理與正確性:

一開始不知道怎麼優化,光想著對下標建樹,怎麼想怎麼不對,後來找了乙份別人的**一看原來是對高度建樹,因為你要找的是符合這個條件

(|hi-hj|>=d)的dp[j],所以你可以把所有i之前的dp[j]都存在相應高度的線段樹端點處(正因為這個才更應該用線段樹),然後每次query(h[i]+d~maxn

)與(1~h[i]-d)的最大的dp[j],然後再來一次單點更新,把a[i]點的值改為dp[j]+1.

另外需要考慮的機試h的範圍為10^15,你直接建線段樹肯定是建不下的,所以要對高度進行離散化,然後對離散化後的高度個數建樹,然後對每個a[i]進行二分查詢,找到它在離散化陣列中的下標,而且還有小於h[i]-d的第乙個數的下標與大於h[i]+d第乙個數的小標,都要用二分查詢,具體怎麼寫的看**。

#include#include#include#include#include#define lson l,mid,rt<<1

#define rson mid+1,r,rt<<1|1

#define ll long long

using namespace std;

const int maxn=100010;

//轉移方程 dp[i]=max(dp[j])+1,(|hi-hj|>=d)

ll mx[maxn<<2],pos[maxn<<2],pre[maxn];

ll a[maxn],b[maxn];

ll n,d;

int cur;

ll maxlen,maxpos;

ll best;

void pushup(int rt)

else

}void update(int q,int c,int i,int l,int r,int rt)

void query(int l,int r,int l,int r,int rt)

return;

}int mid=(l+r)>>1;

if(l<=mid) query(l,r,lson);

if(mid= num) else

}return l;

}int main()

sort(b+1,b+n+1);

for(int i=2; i<=n; i++)

}update(findl(a[1]),1,1,1,cur,1);

int best=1;

int len=1;

pre[1]=0;

for(int i=2; i<=n; i++)

}printf("%d\n",len);

print(best);

cout

}

cf474e Pillars 線段樹優化dp

有n個柱子,每個柱子有乙個高度hi h i,每個柱子可以跳到它後面高度與它相差大於d的柱子 即 h i hj d hi hj d 求最多可以跳多少個柱子an si m ax a nsj 1 hi h j d且 ja ns i ma x an sj 1 h i hj d且 j 因此建立一棵線段樹,節點...

離散化 線段樹

題目 分析 每次1操作會往序列底加first個second,first 和 second 都是最大1e9的資料,每次2操作詢問序列中第first到第second個數的和 一開始就感覺有點像線段樹,輸入資料太大我們可以離線處理把資料離散化下,然後扔到線段樹上,維護兩個陣列 sum 區間數的值的和 nu...

HDU 3607 線段樹 離散化 DP

n個連續的盒子,每個盒子有高度h和價值v,選擇任意一點進入,且從任意一點出來,進入後只能從左向右走,且每次走到的盒子高度必須更高,可以跳過低的盒子 狀態轉移方程 dp i max dp j v i 0 jh j 用線段樹優化,尋找 j include stdio.h include string.h...