終於來寫這個東西了。。
lg 模版:給定 n 個點的數,點有點權, $ m $ 次修改點權,求修改完後這個樹的最大獨立集大小。
我們先來考慮樸素的最大獨立集的 dp
\[dp[u][0] = \sum_v max\\\dp[u][1] = val[u] + \sum_v dp[v][0]
\]現在我們就擁有了乙個 $ o(nm) $ 的做法,但是明顯它不優秀。
既然支援修改,我們可以考慮樹剖(或者lct),現在樹被我們剖成了重鏈和輕鏈。此時發現我們其實可以先對輕子樹算出 dp 值並且累計到當前根,再去算重鏈的影響,這樣有什麼好處呢?好處在於重鏈我們可以單獨算,這樣的話 dp 轉移就是連續的。同時,當你修改乙個點,它所影響的也僅僅是它到根的很多重鏈,不會影響到這路徑上虛兒子的貢獻。
但是如果按照原來的 dp 轉移,修改點權仍然很困難。為此定義了一種新的矩陣乘法,讓兩個矩陣相乘時的乘法改成加法,加法改成最大值,也就是:
\[(a\times b)_ = \text_k\ + b_\}
\]不難發現這樣定義矩陣乘法後矩陣乘法仍然滿足結合律。
然後我們考慮對於乙個點 $ u $ ,假設它已經算完了輕兒子的貢獻,計作 $ g[u][0/1] $ ,考慮從重兒子轉移,那麼這個點的轉移矩陣就是
\[\begin g[u][0]&g[u][0]\\g[u][1]&-\infin \end\times \begindp[u][0]\\dp[u][1]\end
\]為什麼呢?考慮 $ dp[u][0] $ 可以從什麼轉移過來,是由 $ dp[u][0] $ 和 $ dp[u][1] $ 中選擇較大的和 $ g[u][0] $ 加起來得到的,而 $ dp[u][1] $ 由 $ dp[u][0] + g[u][1] $ 得到,故最後乙個位置填 $ -\infin $。
寫到這裡感覺 lct 會比樹剖好寫的多,所以後面預設使用 lct 了。
然後我們考慮對每個鏈維護它的轉移矩陣的乘積。而 $ g $ 的維護就是對乙個鏈的頂端的父親更新 $ g $ 即可,體現在 lct 中就是乙個點維護它 splay 裡面的矩陣的積。注意這裡的矩陣乘法的順序,對乙個鏈做的時候應該從上乘到下,所以 pushup 的時候應該先左後中最後右。
如果我們需要得到乙個點的 dp 值,必須把它 access 並且 旋轉到根,類似 qtree v 的做法,不然它內部的值是假的(是子樹或者splay子樹內的乘積).
修改權值,比較簡單的方法是 lct 直接 旋轉到根 然後直接修改 val 和矩陣就可以了。
感覺比 red blue tree 好寫,不用拿 bst 維護虛兒子資訊。。(然後估計碼一天)
**還是很好看的(雖然調起來很煩)
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
using namespace std;
#define maxn 100006
#define max( a , b ) ( (a) > (b) ? (a) : (b) )
#define inf 0x3f3f3f3f
int n , m;
int w[maxn];
int head[maxn] , to[maxn << 1] , nex[maxn << 1] , ecn;
void ade( int u , int v )
struct mtrx
inline int re( )
inline mtrx operator * ( const mtrx& a ) const
};int ch[maxn][2] , fa[maxn] , dp[maxn][2];
mtrx g[maxn];
inline bool notroot( int u )
inline void pu( int u )
inline void rotate( int u )
//void rotate( int x )
void splay( int x )
}void access( int x )
}void pre( int u , int f )
g[u].in( u[dp][0] , u[dp][1] );
}int main()
}
其實就是板子題,每次詢問,強制選本質上就是權值 inf 強制不選擇就是 -inf
中間掛了幾次。。這題 longlong 得注意。。(矩陣返回值沒開longlong然後wa了兩發。。)
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
using namespace std;
#define maxn 400006
#define max( a , b ) ( (a) > (b) ? (a) : (b) )
#define inf (1ll<<60)
int n , m;
int w[maxn];
int head[maxn] , to[maxn << 1] , nex[maxn << 1] , ecn;
void ade( int u , int v )
struct mtrx
inline long long re( )
inline mtrx operator * ( const mtrx& a ) const
};int ch[maxn][2] , fa[maxn]; long long dp[maxn][2];
mtrx g[maxn];
inline bool notroot( int u )
inline void pu( int u )
inline void rotate( int u )
void splay( int x )
}void access( int x )
}void pre( int u , int f )
g[u].in( u[dp][0] , u[dp][1] );
}long long s;
void mdfy( int u , long long x )
int main()
}
數字dp模版(dp)
1 include 2 include 3 include 4 include 5 6using namespace std 78 intt 9long long dp 19 19 2005 10 long long l,r 11int shu 20 12 13long long dfs int l...
數字dp模版
int dfs int i,int s,bool e f為記憶化陣列 i為當前處理串的第i位 權重表示法,也即後面剩下i 1位待填數 s為之前數字的狀態 如果要求後面的數滿足什麼狀態,也可以再記乙個目標狀態t之類,for的時候列舉下t e表示之前的數是否是上界的字首 即後面的數能否任意填 for迴圈...
數字dp的模版
typedef long long ll int a 20 ll dp 20 state 不同題目狀態不同 ll dfs int pos,state變數 bool lead 前導零 bool limit 數字上界變數 不是每個題都要判斷前導零 計算完,記錄狀態 if limit lead dp po...