dp的一種理解角度及 NOI2020 命運 題解

2022-08-21 20:36:14 字數 2445 閱讀 2173

在apio2019的時候,乙個講座的主題是「用集合的角度理解dp」,雖然當時講的很水(真的很水),但是後來思考了一下,這個角度其實還是有一些**價值的,不過更像是從「生成函式」的角度理解。

我們考慮乙個簡單的揹包dp,求選\(i\)個物品的方案數,每個物品有個數\(d_k\)。\(f_i\)表示選\(i\)個的方案數,轉移方程對任意\(i,j\)都有\(f_ = f_i * f_j\)。這個很容易理解。但是為了拓展,我們把這個乘法這樣看待:我們把乙個狀態\(f_i\)視作所有選\(i\)個物品的方案的「集合」,既每種選法的方案數之和\((v_1+v_2 \cdots +v_n)\)的形式,其中每個\(v\)的值顯然就是一種方案中選擇的那特定\(i\)個物品個數的乘積\(d_*d_\cdots\),我們把它叫做這個方案的權值。這樣\(f_i*f_j\)的意義就是:在大小為\(i\)的所有方案中和大小為\(j\)的所有方案中各選擇乙個拼起來,拼起來的新方案權值是兩者的權值乘積。不難發現兩個方案拼起來以後的權值還是對應的選法數,而新的dp狀態\(f_\)就包含了所有選法,也可以看成\((v_1+v_2 \cdots +v_n)\)的形式,可以繼續計算。可以發現,這個方法的本質就是:把可以進行乘法合併的部分(方案數)作為係數,把通過加法合併的部分(選的個數)作為指數,手動進行多項式乘法。而更加複雜的dp也可以抽象成這種形式,只不過多項式係數意義不同,多項式的每一項可能不止是\(kx^n\),可能是\(kx^ny^mz\),而合併的時候也不一定是正常的卷積,也可以是\(\min \max\)卷積,位運算卷積等。

考慮拓展這種形式,我們以\(noi2020\)中\(day1t2\)為例子()。這道題中,考慮一種暴力的容斥:選擇乙個鏈的集合計算貢獻。我們發現乙個集合的貢獻形如\((-1)^*方案數*2^\)。其中方案數就是所有選的鏈數和邊集並大小都互相相同的選法數目。我們嘗試把這個視為它的權值記錄在\(dp\)方案裡,發現兩個方案的權值不能通過乘法合併!這時我們就可以考慮:把其中的某項移到外面單獨記錄,也就是增加dp陣列的維度。由於方案數和容斥係數在乘起來以後都可以正確合併,我們不妨就在dp中記錄邊集並大小,最後在乘上去。可以發現這就是乙個提取同類項的過程。事實上,這是由於邊集並的貢獻採用的運算形式為\(2^x * 2^y = 2^\),不符合乘法的規則,所以我們只能進行手動處理。

這樣,我們初步的設計了乙個dp:\(f_\)表示選的鏈並大小為\(j\)的所有方案的權值和。我們發現,如果在選擇一條鏈以後直接把他的邊集大小加上去,那麼就會進行重複計算。而記錄當前的邊並集情況需要指數級別的狀態。這時,我們可以發現題目的性質有所有鏈都是直上直下的,也就是說我們如果從下到上選擇這些鏈,只需要知道當前的所有鏈中端點深度最小的是多少就可以知道新的鏈與原來集合的並大小。

所以,乙個可行的dp狀態為:\(f_\)表示(只選擇下端點在\(i\)子樹中的鏈,他們的邊集並大小為\(j\),且選擇的所有鏈中最淺的上端點深度為\(k\))的所有方案的權值和,這裡的權值是\((-1)^*方案數\),因為我們把不能用乘法合併的部分提出去記錄到了dp陣列裡,最後計算答案的時候再乘上去。而轉移就很簡單了,判斷合併的兩個方案深度決定是否增加並集大小,然後直接使用乘法即可。

為了降低複雜度,我們進一步考慮如何合併\(2^\)。如果你做的題比較多或者腦子比較好使,就會知道「轉概率」的方法。也就是把所有dp值都除乙個\(2^\),最後在乘回去。這樣,乙個集合的貢獻就是\((-1)^*方案數* \frac^\)。這樣,我們就可以直接合併兩個不相交的集合了。所以,我們令dp中的邊集並大小只包含子樹中的邊,在合併的時候再考慮是否加入子樹的父邊。這樣的話,來自兩個子樹的並一定不相交,可以直接做乘法合併。而新加入一條邊我們可以直接用除2的方法實現貢獻處理。

這樣,我們的dp只需要記錄端點深度了。如果從正常角度思考,由於這樣的dp記錄的不只是方案數,還有更複雜的貢獻資訊,很難理解這麼做直接把dp值乘起來的正確性。但是把dp值視為集合以後,把相乘看作方案之間兩兩合併就很容易設計優化演算法和證明正確性。\(dp_\)表示的是在\(i\)子樹中選鏈的所有方法中,選擇的鏈最淺的點深度為\(j\)的所有方案的權值和。可以發現,因為兩個方案的權值可以通過乘法合併,所以兩個記錄方案和的dp狀態做乘法的意義等價於將兩種特徵的方案兩兩合併再相加,與前文的揹包問題是一樣的。

所以,我們得到了乙個簡單的\(o(n*\min(n,m))\)的演算法。事實上,用生成函式的角度, 把dp裡的值看作多項式係數,把dp陣列的維度記錄的值看作多項式的指數和變數(\(x^ny^m\)中的\(x,y,n,m\))的話,這道題裡優化後的dp陣列\(dp_\),列出方程後我們發現這個dp其實就是把\(j\)視作\(x^j\),然後做\(\min\)卷積,既合併\(f_與f_\)時新的狀態為\(f_\)。而在樹上做這種形式的dp可以用線段樹合併優化,具體可以參考題目。這樣,我們就用\(o(n\log n)\)的時間複雜度解決了本題。

所以,通過定義廣義的「運算」,我們可以更深刻的理解dp:把遵循不同運算規則的生成函式項放在dp的每一維,(其中陣列裡的數值也是一維,通常存放的是可以用乘法,加法合併的簡單貢獻),然後進行運算。而這些維度事實上是可以隨意交換的,所以用這種角度觀察是我們優化時間複雜度,提取同類項簡化dp的有利方法,可以嘗試使用。

理解泰勒展開的一種角度

我們可以用乙個多項式函式去逼近乙個連續可導的函式,泰勒展開就是求這樣乙個多項式函式。用乙個函式去逼近乙個函式,函式是一系列值,不是單個值,如果是單個值,那麼很簡單的直接讓其相等即可,但是若是一系列值,且是連續可導的,自然的,除了讓逼近的函式和原函式在某處的值相等以外,還需要讓其變化趨勢也一樣,這樣才...

從另一種角度理解量子力學

量子力學的教學,有兩種方法。第一種方法是如今大多數物理學家採用的,以歷史發展的順序,先從經典力學講到電動力學,解一大堆微分方程,然後告訴你黑體輻射悖論和那些奇奇怪怪的實驗結果,這些導致了物理學的危機。然後你會學習那些物理學家在1900和1926年之間提出來的各種複雜想法來解救這個危機。幸運的話,經過...

NOI 20 求一元二次方程的根

描述 利用公式x1 b sqrt b b 4 a c 2 a x2 b sqrt b b 4 a c 2 a 求一元二次方程ax2 bx c 0的根,其中a不等於0。輸入 輸入一行,包含三個浮點數a,b,c 它們之間以乙個空格分開 分別表示方程ax2 bx c 0的係數。輸出 輸出一行,表示方程的解...