hdu 3450 離散化 dp 線段樹優化

2021-07-04 22:30:52 字數 1973 閱讀 2733

題意:

給你一串長度為n的序列,給乙個d,要求找出有幾個子串行能夠滿足兩個相鄰的元素之間差值不超過d。

思路:

dp。定義dp[i]表示以第i個為結束的滿足條件的子串行的個數。

轉移方程:dp

[i]=

(∑i−

1j=1

dp[j

])+1

(abs

(num

[i]−

num[

j])<=d)

答案就是dp陣列的總和最後扣掉n就可以了。

此時會發現更新的時間複雜度是o(

n2) ,這個顯然是過不了的。

轉移的複雜度是o(

n),但是會發現每次轉移的就是乙個區間的和~,可以想到運用線段樹來優化這個部分,將複雜度降至o(

logn

) ,用線段樹維護當前以這個a[i]值為結尾的子串行的個數,每次查詢a[i-d] ~ a[i+d]這個區間的和。

但是這裡又有乙個問題,就是這個a[i]的值是在int 範圍內的,也就是說這個線段樹要開109

這個數量級的空間,顯然是不可以的。

這時候又該想到離散化處理,因為我們只需要有這個值之間的相互關係。對於具體的值並不是太有用,將每乙個數對應乙個id,因為只有n個數,這樣就將109

縮小為106

,最後通過這個id來對其進行操作。

查詢出a[i]-d、a[i]+d的id,然後查詢這個區間的和用來更新dp[i]。 再查詢出a[i]的id,將dp[i]+1更新到線段樹之中去。

離散化處理:

可以將原來的元素序列複製乙份為id陣列,排序去重,之後運用二分查詢來查詢離散化後的id。

#include 

#include

#include

#include

using

namespace

std;

#define m 100009

#define mod 9901

int n,d;

int dat[m<<2];

int dp[m];

int id[m];

int b[m];

void pushup(int rt)

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

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

build(l,mid,rt<<1);

build(mid+1,r,rt<<1|1);

pushup(rt);

}void update(int a,int b,int l,int r,int rt)

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

if(a <= mid) update(a,b,l,mid,rt<<1);

else update(a,b,mid+1,r,rt<<1|1);

pushup(rt);

}int query(int a,int b,int l,int r,int rt)

int ret = 0;

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

if(a <= mid) ret = (ret+query(a,b,l,mid,rt<<1))%mod;

if(b > mid) ret = (ret+query(a,b,mid+1,r,rt<<1|1))%mod;

return ret%mod;

}int main()

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

int m = unique(id+1,id+n+1) - (id+1);

build(1,m,1);

int ans = 0;

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

printf("%d\n",ans%mod);

}return

0;}

HDU 3607 線段樹 離散化 DP

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

HDU 3333 線段樹 離散化

只查詢區間不同的數的和 思路好題 對查詢離線 不斷的往每個位置插值 並把前面位置的值置為0 每查到乙個右端點 查詢一下 等價操作的轉換 離散化一下 include define mem a,b memset a,b,sizeof a define lson root 1 define rson ro...

HDU1199 離散化線段樹

題目大意 一段長度未知的線段,一種操作 a b c 表示區間 a,b 要塗的顏色,c w塗白色,c b塗黑色,問你最長的白色區間段時多長。解題思路 就快去南京邀請賽了,最近做題超沒狀態,cf rating一直掉,這麼簡單的線段樹離散化居然搞了我乙個晚上,糾結。開始用線段樹區間合併的方法做,wa到死,...