日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【学习笔记】浅谈广义矩阵乘法——动态DP

發布時間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【学习笔记】浅谈广义矩阵乘法——动态DP 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 廣義矩陣乘法
  • 動態DP
  • 例題:洛谷4719

以下內容是本人做題經驗,如有雷同,純屬抄襲;如有不對,純屬不懂,還請指正

廣義矩陣乘法

眾所周知,矩陣滿足乘法交換律,前一個矩陣的列必須是后一個矩陣的行
n×pn\times pn×pAAA矩陣和p×mp\times mp×mBBB矩陣相乘得到CCC矩陣
Ci,j=∑k=1pAi,k×Bk,jC_{i,j}=\sum_{k=1}^pA_{i,k}\times B_{k,j}Ci,j?=k=1p?Ai,k?×Bk,j?
假設矩陣D=A?B?CD=A*B*CD=A?B?C,考慮DDD是如何生成的
Di,j=∑k=1n((∑p=1nAi,p×Bp,k)×Ck,j)D_{i,j}=\sum_{k=1}^n\left ( (\sum_{p=1}^nA_{i,p}\times B_{p,k}) \times C_{k,j} \right)Di,j?=k=1n?((p=1n?Ai,p?×Bp,k?)×Ck,j?)=∑k=1n(∑p=1n(Ai,p×Bp,k×Ck,j))=\sum_{k=1}^n\left ( \sum_{p=1}^n(A_{i,p}\times B_{p,k} \times C_{k,j})\right)=k=1n?(p=1n?(Ai,p?×Bp,k?×Ck,j?))=∑k=1n(Ai,p×∑p=1n(Bp,k×Ck,j))=\sum_{k=1}^n\left ( A_{i,p}\times \sum_{p=1}^n(B_{p,k} \times C_{k,j})\right)=k=1n?(Ai,p?×p=1n?(Bp,k?×Ck,j?))=A?(B?C)=A*(B*C)=A?(B?C)


性質
如果把乘法和加法換成其他運算,opt1,opt2opt1,opt2opt1,opt2
只要滿足opt1對opt2滿足分配律,則其也滿足矩陣的結合律

就好比乘法對加法滿足分配律
因為a?(b+c)a*(b+c)a?(b+c),與aaa先乘進括號再相加的答案a?b+a?ca*b+a*ca?b+a?c一樣

opt1:仁兄,穩住
——opt2:你要搞爪子,我憋不住了
opt1:不要占著茅坑,先讓我來
然后opt1解放了,離開了,只剩下opt2繼續解決

再舉個減法和取最大值的例子
我們知道max(b?a,c?a)=max(b,c)?amax(b-a,c-a)=max(b,c)-amax(b?a,c?a)=max(b,c)?a
所以我們也可以說減法對取最大值滿足分配律

有了這個進階版本,我們就可以將一般平常寫的矩陣中的乘法和加法操作換掉


動態DP

很多題目并不是靜態的,往往伴隨著修改原內容的操作,導致DP值不再正確

暴力的想法,則是每次修改,就重新做一遍DP

顯然這樣除了TLE別無出路

然而這個時候——動態DP孕育而生

動態DP,如其名,生來就是解決這種改變影響之前的DP值的問題

一半來說優化算法都是往logloglog級下壓

動態DP就是借助廣義矩陣來進行快速優化的

這類題目,原始的轉移一定可以用矩陣乘法來加速做

然后修改,就變成了加速矩陣的修改

一般還會在線段樹上進行矩陣的合并


例題:洛谷4719

Step1:考慮沒有修改

就是一道非常板的樹形DP

f[i][0/1]f[i][0/1]f[i][0/1]表示是否選iii時,子樹iii的最大值,111選,000不選
{f[i][0]=∑j∈sonimax(f[j][1],f[j][0])f[i][1]=∑j∈sonif[j][0]\left\{ \begin{aligned} f[i][0]=\sum_{j∈son_i}max(f[j][1],f[j][0])\\ f[i][1]=\sum_{j∈son_i}f[j][0]&&&&&&&&&&&&&&&&&&&&&&&&&\\ \end{aligned} \right. ??????????f[i][0]=jsoni??max(f[j][1],f[j][0])f[i][1]=jsoni??f[j][0]??????????????????????????


Step2:考慮特殊的鏈形式,假設仍然沒有修改

也就是說iii只有一個兒子jjj,就沒有之前所謂的求和罷了
{f[i][0]=max(f[j][1],f[j][0])f[i][1]=f[j][0]+w[i]\left\{ \begin{aligned} f[i][0]=max(f[j][1],f[j][0])\\ f[i][1]=f[j][0]+w[i]&&&&&&&&&&&&&&&&&&&&&&&&&\\ \end{aligned} \right. {f[i][0]=max(f[j][1],f[j][0])f[i][1]=f[j][0]+w[i]??????????????????????????
這里就可以用到前面所說的加法對于求最大值有分配律

人為的給f[i][1]f[i][1]f[i][1]轉移添加一個最大值,f[i][1]=max(f[j][0])+w[i]f[i][1]=max(f[j][0])+w[i]f[i][1]=max(f[j][0])+w[i]

那么就可以構造矩陣進行矩陣加速了

矩陣相乘時,為了防止f[j][1]f[j][1]f[j][1]f[i][1]f[i][1]f[i][1]造成貢獻

此題可以將其矩陣位置上設個極小值

[00w[i]?INF]×[f[j][0]f[j][1]]=[f[i][0]f[i][1]]\begin{bmatrix} 0&0\\ w[i]&-INF\\ \end{bmatrix}\times \begin{bmatrix} f[j][0]\\ f[j][1] \end{bmatrix}=\begin{bmatrix} f[i][0]\\ f[i][1]\\ \end{bmatrix}[0w[i]?0?INF?]×[f[j][0]f[j][1]?]=[f[i][0]f[i][1]?]


Step3:考慮一般形式,仍然沒有修改

可以將加速矩陣重新構造為
[f′[i][0]f′[i][0]f′[i][1]?INF]×[f[j][0]f[j][1]]=[f[i][0]f[i][1]]\begin{bmatrix} f'[i][0]&f'[i][0]\\ f'[i][1]&-INF\\ \end{bmatrix}\times \begin{bmatrix} f[j][0]\\ f[j][1] \end{bmatrix}=\begin{bmatrix} f[i][0]\\ f[i][1]\\ \end{bmatrix}[f[i][0]f[i][1]?f[i][0]?INF?]×[f[j][0]f[j][1]?]=[f[i][0]f[i][1]?]

  • f′[i][0]f'[i][0]f[i][0]表示除了兒子jjj貢獻之外的f[i][0]f[i][0]f[i][0]
  • f′[i][1]f'[i][1]f[i][1]表示除了兒子jjj貢獻之外的f[i][1]f[i][1]f[i][1]

意思就是我們要將兒子分為兩種,一種是特殊轉移的,另一種就是剩下的

也就是說這個兒子灰常忒殊,加上我們已經會做鏈的形式了

自然而然想到樹鏈剖分中的重兒子

對于重兒子直接維護這個矩陣,一條重鏈的矩乘可以用線段樹壓為logloglog

輕兒子就暴力轉移即可

輕兒子暴力轉移一次到父親,然后父親就爬自己所在的那條重鏈


Step4:現在考慮修改

有了樹鏈剖分,修改也變得可操作了

假設修改了iii點,除了iii和其父親的fff會受到影響,其余是不變的

所以我們只需要修改子樹iiiiii的祖先的fff值即可

首先更新不考慮重兒子的f[i][1]f[i][1]f[i][1]的值,更新iii的矩陣(每個點都有一個加速矩陣)

然后對iii所在重鏈,進行單點修改操作,更新fff和矩陣

然后爬到新的重鏈,設到達的點為xxx,之前重鏈的頂端對新的重鏈而言是輕兒子

所以要更新f′[x][0]f'[x][0]f[x][0]f′[x][1]f'[x][1]f[x][1]

以此類推,一路往上修改,時間復雜度O(log?n2)O(\log n^2)O(logn2)

#include <cstdio> #include <vector> #include <cstring> #include <iostream> using namespace std; #define inf 1e12 #define maxn 100005 #define int long long struct matrix {int c[2][2];matrix(){}matrix( int x1, int x2, int x3, int x4 ) {c[0][0] = x1, c[0][1] = x2, c[1][0] = x3, c[1][1] = x4;}void clear() {memset( c, 0, sizeof( c ) );}int *operator [] ( int x ) { return c[x]; }matrix operator * ( matrix v ) const {matrix ans;ans.clear();for( int i = 0;i < 2;i ++ )for( int j = 0;j < 2;j ++ )for( int k = 0;k < 2;k ++ )ans[i][j] = max( ans[i][j], c[i][k] + v[k][j] );return ans;} }tree[maxn << 2], tmp[maxn]; vector < int > G[maxn]; int n, m, cnt; int a[maxn]; int siz[maxn], f[maxn], son[maxn]; int dfn[maxn], id[maxn], top[maxn], bot[maxn]; int dp[maxn][2];void dfs1( int u, int fa ) {siz[u] = 1, f[u] = fa;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == fa ) continue;dfs1( v, u );siz[u] += siz[v];if( siz[v] > siz[son[u]] || ! son[u] )son[u] = v;} }void dfs2( int u, int t ) {top[u] = t, dfn[u] = ++ cnt, id[cnt] = u;if( son[u] ) dfs2( son[u], t ), bot[u] = bot[son[u]];else bot[u] = u;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == f[u] || v == son[u] ) continue;else dfs2( v, v );} }void dfs( int u ) {dp[u][0] = 0, dp[u][1] = a[u];for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == f[u] ) continue;dfs( v );dp[u][1] += dp[v][0];dp[u][0] += max( dp[v][1], dp[v][0] );} }void build( int num, int l, int r ) {if( l == r ) {int u = id[l], f1 = a[u], f0 = 0;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == f[u] || v == son[u] ) continue;f1 += dp[v][0], f0 += max( dp[v][0], dp[v][1] );}tree[num] = tmp[l] = matrix( f0, f0, f1, -inf );return;}int mid = ( l + r ) >> 1;build( num << 1, l, mid );build( num << 1 | 1, mid + 1, r );tree[num] = tree[num << 1] * tree[num << 1 | 1]; }void modify( int num, int l, int r, int pos ) {if( l == r ) { tree[num] = tmp[l]; return; }int mid = ( l + r ) >> 1;if( pos <= mid ) modify( num << 1, l, mid, pos );else modify( num << 1 | 1, mid + 1, r, pos );tree[num] = tree[num << 1] * tree[num << 1 | 1]; }matrix query( int num, int l, int r, int L, int R ) {if( L <= l && r <= R ) return tree[num];int mid = ( l + r ) >> 1;if( R <= mid ) return query( num << 1, l, mid, L, R );else if( mid < L ) return query( num << 1 | 1, mid + 1, r, L, R );else return query( num << 1, l, mid, L, R ) * query( num << 1 | 1, mid + 1, r, L, R ); }void modify( int u, int w ) {tmp[dfn[u]][1][0] += w - a[u], a[u] = w;while( u ) {matrix last = query( 1, 1, n, dfn[top[u]], dfn[bot[u]] );modify( 1, 1, n, dfn[u] );matrix now = query( 1, 1, n, dfn[top[u]], dfn[bot[u]] );u = f[top[u]];if( ! u ) break;int p = dfn[u];tmp[p][0][0] = tmp[p][0][1] = tmp[p][0][0] + max( now[0][0], now[1][0] ) - max( last[0][0], last[1][0] );tmp[p][1][0] = tmp[p][1][0] + now[0][0] - last[0][0];} }signed main() {scanf( "%lld %lld", &n, &m );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );for( int i = 1, u, v;i < n;i ++ ) {scanf( "%lld %lld", &u, &v );G[u].push_back( v );G[v].push_back( u ); }dfs1( 1, 0 );dfs2( 1, 1 );dfs( 1 );build( 1, 1, n );for( int i = 1, x, y;i <= m;i ++ ) {scanf( "%lld %lld", &x, &y );modify( x, y ); matrix ans = query( 1, 1, n, 1, dfn[bot[1]] );printf( "%lld\n", max( ans[0][0], ans[1][0] ) );}return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的【学习笔记】浅谈广义矩阵乘法——动态DP的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。