題意:給你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...