姑且叫做時間戳優化吧
我們會遇到一類問題:
給你一棵n<=5000的樹,要在樹上跑樹形dp。我們可以輕鬆的想出dp的定義式,第一維是代表節點,第二維是題目要維護的資訊。範圍均是5000。
如果我們暴力去跑的話,對於每個節點都要列舉所有子節點,並且第二維要列舉當前節點的值和子節點的值。
這樣效率是o(n
3)
o(n^3)
o(n3
)顯然是不夠的。
我們可以加入乙個優化,考慮當前子樹的size,子樹如果比較小,第二維就沒有必要列舉那麼多。更進一步,我們不要預處理每個子樹的size,在dp的時候不斷更新,每次的size只有已經遞迴過的子樹和根節點,這樣子合併的效率是o(n
2)
o(n^2)
o(n2)
例題1.cf 1499f
題意:給定一顆樹,讓你找出乙個邊集的數量,邊集需滿足:去掉集合中的邊,剩下的所有子樹的直徑均不大於給定的k。
思路:我們要保證不能出現有乙個子樹到根的最長路徑len1和另乙個子樹到根的最長路徑len2使得len1+len2>k。
為了判斷這個情況我們就能構建乙個dp定義式
dp[u][len]代表以x為根的子樹,最長到達根u的路徑長度為len的邊集數量。
我們在轉移的時候,有兩種情況。
1.u-v這條邊不連線,那麼直接先前的答案*這個子樹的答案即可
2.u-v這條邊連線,為了保證最開始的條件,轉移的時候要避免這樣的答案合併。
乙個小trick:每次先把當前的dp值先拎出來,轉移完再放回去,這樣不用考慮類似01揹包那樣變數迴圈的方向問題。
const
int maxn=
5e3+5;
const ll mod=
998244353
;int head[maxn]
,tot;
struct nng[maxn<<1]
;void
add_edge
(int u,
int v)
;head[u]
=tot;
}ll dp[maxn]
[maxn]
,sz[maxn]
,n,mx,now[maxn]
;void
dfs(
int u,
int fa)
sz[u]
+=sz[v];}
}int
main()
dfs(1,
0); ll ans=0;
for(
int i=
0;i<=mx;i++
)ans=
(ans+dp[1]
[i])
%mod;
printf
("%lld\n"
,ans)
;return0;
}
例題2. 2020icpc南京站-m
題意:給定一顆n<=3000的樹,每個節點上有乙個怪獸,怪獸生命值為hp[i],你必須先擊殺乙個節點的父親節點上的怪獸才能擊殺這個節點的怪獸。且擊殺當前節點的代價是當前節點的怪獸生命值和所有子節點的怪獸生命值之和。
若給定魔力值m,代表你可以選擇m個怪獸直接殺死且無任何代價,求m=0到n的最小代價。
思路:轉換一下問題,我們先計算出m=0的代價,跑樹上dp來求出最大減少的代價值。
dp定義馬上就能蹦出來,dp[u][x]代表u子樹用x次魔力的代價。
仔細思考發現並不方便轉移,於是可以得到這樣的定義式
dp[u][x][0]代表u子樹用x次魔力且沒有殺死u這個節點的最大規避代價。
dp[u][x][1]代表u子樹用x次魔力且殺死u這個節點的最大規避代價。
同樣使用之前所說的優化可以將時間降至o(n
2)
o(n^2)
o(n2
)親身經歷:sz[u]+=sz[v]這個過程一定要放在後面,放前面就是爆t。
賽中**沒有拎出來直接寫了
const
int maxn=
3e4+5;
int head[maxn]
,tot;
struct nng[maxn<<1]
;void
add_edge
(int u,
int v)
;head[u]
=tot;
}ll n,f[maxn]
,a[maxn]
,sz[maxn]
,sum,d[maxn]
;ll dp[maxn]
[maxn][2
];void
dfs(
int u,
int fa)
for(
int i=head[u]
;i;i=g[i]
.nxt)
if(j>=1)
}}sz[u]
+=sz[v];}
}int
main()
return0;
}
部分樹形DP的優化
有一棵n個節點的樹,樹上每個節點有乙個值,選擇m個節點使這些節點值的和最大 要求 如果選當前節點,則必須選它的父節點 我們設dp i j 為以i為根的樹上留j個節點的最大值,轉移方法如下 for int j min q,size x j 1 j 複雜度o n m 2 有一棵n個節點的樹,樹上每個節點...
樹形DP 樹形DP四例
是時候練一下dp了!我的題單 portkey f u,if fu,i 表示以u uu為根節點的子樹中保留i ii條樹枝的最大蘋果數 f u,i max f max f fu,i max這些題是菜,但也不能輕視啊!include using namespace std define in read i...
長鏈剖分優化樹形dp
apio鐵牌告辭 開場想打暴力然後gedit碼 5個小時沒寫完三題最低檔暴力真是快樂 聽課也就學到了一丟丟這個東西。模板題 首先k級兄弟可以一遍dfs的時候丟到k級父親上變成求k級孩子的詢問。求k級孩子有個很簡單的做法,直接dfs的時候維護掃到某個點時記錄下的每種dep出現次數,對於所有求這個點k級...