线性代数四之动态DP(广义矩阵加速)——Can you answer these queries III,保卫王国
動態DP——廣義矩陣加速
- SP1716 GSS3 - Can you answer these queries III
- description
- solution
- code
- [NOIP2018 提高組] 保衛王國
- description
- solution
- code
動態DP能矩陣加速要滿足外層操作符對內層操作符具有分配率
加法對于乘法就具有分配率(a+b)*c=a*c+b*c
SP1716 GSS3 - Can you answer these queries III
description
solution
設fi:f_i:fi?: 前iii個數的最大子段和,gi:g_i:gi?: 以iii結尾的最大子段和
gi=max?(gi?1+ai,ai)g_i=\max(g_{i-1}+a_i,a_i)gi?=max(gi?1?+ai?,ai?)
fi=max(fi?1,gi)f_i=max(f_{i-1},g_i)fi?=max(fi?1?,gi?)
max?\maxmax對于+++具有分配率max(a+c,b+c)=c+max(a,b)
所以廣義的矩陣加速就是在原始的矩陣上?→+*\rightarrow +?→+,+→max?+\rightarrow \max+→max
為了套用矩陣乘法,改寫一下gi=max?(gi?1+ai,ai,?inf),fi=max?(fi?1,gi,?inf)g_i=\max(g_{i-1}+a_i,a_i,-inf),f_i=\max(f_{i-1},g_i,-inf)gi?=max(gi?1?+ai?,ai?,?inf),fi?=max(fi?1?,gi?,?inf)
[fi?1gi?10]×[0aiai?infaiai?inf?inf0]=[figi0]\begin{bmatrix} f_{i-1}\\ g_{i-1}\\ 0\\ \end{bmatrix} \times \begin{bmatrix} 0&a_i&a_i\\ -inf&a_i&a_i\\ -inf&-inf&0\\ \end{bmatrix}= \begin{bmatrix} f_i\\ g_i\\ 0 \end{bmatrix} ???fi?1?gi?1?0????×???0?inf?inf?ai?ai??inf?ai?ai?0????=???fi?gi?0????
code
#include <cstdio> #include <cstring> #include <iostream> using namespace std; #define maxn 50005 #define inf 0x3f3f3f3f struct matrix {int c[3][3];matrix operator * ( const matrix &t ) const {matrix ans;for( int i = 0;i < 3;i ++ )for( int j = 0;j < 3;j ++ )ans.c[i][j] = -inf;for( int i = 0;i < 3;i ++ )for( int j = 0;j < 3;j ++ )for( int k = 0;k < 3;k ++ )ans.c[i][j] = max( ans.c[i][j], c[i][k] + t.c[k][j] );return ans;} }t[maxn << 2], ret; int n, Q; int a[maxn];void New( int num, int pos ) {t[num].c[0][0] = t[num].c[0][1] = t[num].c[2][0] = t[num].c[2][1] = a[pos];t[num].c[0][2] = t[num].c[1][0] = t[num].c[1][2] = -inf;t[num].c[1][1] = t[num].c[2][2] = 0; }void build( int num, int l, int r ) {if( l == r ) {New( num, l );return;}int mid = ( l + r ) >> 1;build( num << 1, l, mid );build( num << 1 | 1, mid + 1, r );t[num] = t[num << 1] * t[num << 1 | 1]; }void modify( int num, int l, int r, int pos ) {if( l == r ) {New( num, pos );return;}int mid = ( l + r ) >> 1;if( pos <= mid ) modify( num << 1, l, mid, pos );else modify( num << 1 | 1, mid + 1, r, pos );t[num] = t[num << 1] * t[num << 1 | 1]; }matrix query( int num, int l, int r, int L, int R ) {if( L <= l && r <= R ) return t[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 ); }int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )scanf( "%d", &a[i] );build( 1, 1, n );scanf( "%d", &Q );while( Q -- ) {int opt, l, r;scanf( "%d %d %d", &opt, &l, &r );if( ! opt ) a[l] = r, modify( 1, 1, n, l );else {ret = query( 1, 1, n, l, r );printf( "%d\n", ret.c[2][1] );}} return 0; }[NOIP2018 提高組] 保衛王國
description
solution
考慮對于一條鏈的簡單轉移,設dpi,0/1:idp_{i,0/1}:idpi,0/1?:i點不選/選的最小花費
矩陣形式:
[fi?1,0fi?1,1]×[?inf0aiai]=[fi,0fi,1]\begin{bmatrix} f_{i-1,0}\\ f_{i-1,1}\\ \end{bmatrix} \times \begin{bmatrix} -inf&0\\ a_i&a_i\\ \end{bmatrix}= \begin{bmatrix} f_{i,0}\\ f_{i,1}\\ \end{bmatrix} [fi?1,0?fi?1,1??]×[?infai??0ai??]=[fi,0?fi,1??]
在樹上,就是普通的樹形DPDPDP,設dpi,0/1:idp_{i,0/1}:idpi,0/1?:i子樹內iii點不選/選的最小花費
dpi,0=∑j∈sonidpj,1dp_{i,0}=\sum_{j\in son_i}dp_{j,1}dpi,0?=∑j∈soni??dpj,1?
dpi,1=∑j∈sonimin?(dpj,0,dpj,1)dp_{i,1}=\sum_{j\in son_i}\min(dp_{j,0},dp_{j,1})dpi,1?=∑j∈soni??min(dpj,0?,dpj,1?)
對于某個點轉移是固定的且min?\minmin對+++同樣具有分配率
考慮動態DPDPDP,樹鏈剖分,輕重鏈斷樹,將矩陣放到線段樹上合并加速
gi,0=∑j∈soni,j≠MaxSonifj,1g_{i,0}=\sum_{j\in son_i,j≠MaxSon_i}f_{j,1}gi,0?=∑j∈soni?,j?=MaxSoni??fj,1?
gi,1=∑j∈soni,j≠MaxSonimin?(fj,0,fj,1)g_{i,1}=\sum_{j\in son_i,j≠MaxSon_i}\min(f_{j,0},f_{j,1})gi,1?=∑j∈soni?,j?=MaxSoni??min(fj,0?,fj,1?)
矩陣形式:
[fMaxSoni,0fMaxSoni,1]×[?infgi,0gi,1gi,1]=[fi,0fi,1]\begin{bmatrix} f_{MaxSon_i,0}\\ f_{MaxSon_i,1}\\ \end{bmatrix} \times \begin{bmatrix} -inf&g_{i,0}\\ g_{i,1}&g_{i,1}\\ \end{bmatrix}= \begin{bmatrix} f_{i,0}\\ f_{i,1}\\ \end{bmatrix} [fMaxSoni?,0?fMaxSoni?,1??]×[?infgi,1??gi,0?gi,1??]=[fi,0?fi,1??]
一條重鏈頂點的DPDPDP值就是這條鏈上所有矩陣“乘積”
具體細節詳情請見完美代碼,非常好懂,只是長了點而已
code
#include <cstdio> #include <vector> #include <cstring> #include <iostream> using namespace std; #define inf 1e18 #define int long long #define maxn 100005 vector < int > G[maxn]; char type[4]; int n, m, cnt; int p[maxn], siz[maxn], fa[maxn], son[maxn], dfn[maxn], id[maxn], top[maxn], rnk[maxn], Left[maxn], Right[maxn], dep[maxn]; int f[maxn][2], g[maxn][2], ans[2][2], MS[2][2]; int matrix[maxn << 2][2][2];void dfs1( int u, int father ) {siz[u] = 1, fa[u] = father, dep[u] = dep[father] + 1, f[u][1] = p[u];for( auto v : G[u] ) {if( v == father ) continue;else dfs1( v, u );siz[u] += siz[v];f[u][0] += f[v][1];f[u][1] += min( f[v][0], f[v][1] );if( ! son[u] || siz[v] > siz[son[u]] )son[u] = v;} } /* f[x][0/1]:x不選/選 f[x][0]+=f[son][1] f[x][1]+=min(f[son][0],f[son][1]) g[x][0]:定義等同于f[x][0]但只含輕兒子 g[x][1]:定義等同于f[x][1]但只含輕兒子 left[x]-right[x]:一條重鏈所管轄的區間(dfn序) */ void dfs2( int u, int t ) {dfn[u] = Left[u] = Right[u] = ++ cnt, id[cnt] = u, top[u] = t;g[u][1] = p[u];if( ! son[u] ) return;else dfs2( son[u], t );Right[u] = Right[son[u]];for( auto v : G[u] ) {if( v == fa[u] || v == son[u] ) continue;else dfs2( v, v );g[u][0] += f[v][1];g[u][1] += min( f[v][0], f[v][1] );} } /* 動態dp * -> + + -> min */ void pushup( int num ) {for( int i = 0;i < 2;i ++ )for( int j = 0;j < 2;j ++ )matrix[num][i][j] = inf;for( int i = 0;i < 2;i ++ )for( int j = 0;j < 2;j ++ )for( int k = 0;k < 2;k ++ )matrix[num][i][j] = min( matrix[num][i][j], matrix[num << 1][i][k] + matrix[num << 1 | 1][k][j] ); } /* |inf A[u]| |B[u] B[u]| * |f[maxson][0]| |f[maxson][1]| = f[u][0] f[u][1] */ void build( int num, int l, int r ) {if( l == r ) {rnk[id[l]] = num;matrix[num][0][0] = inf;matrix[num][0][1] = g[id[l]][0];matrix[num][1][0] = matrix[num][1][1] = g[id[l]][1];return;}int mid = ( l + r ) >> 1;build( num << 1, l, mid );build( num << 1 | 1, mid + 1, r );pushup( num ); }void modify( int num, int l, int r, int pos ) {if( l == r ) return;int mid = ( l + r ) >> 1;if( pos <= mid ) modify( num << 1, l, mid, pos );else modify( num << 1 | 1, mid + 1, r, pos );pushup( num ); }void query( int num, int l, int r, int L, int R ) {if( L <= l && r <= R ) {for( int i = 0;i < 2;i ++ )for( int j = 0;j < 2;j ++ )MS[i][j] = inf;for( int i = 0;i < 2;i ++ )for( int j = 0;j < 2;j ++ )for( int k = 0;k < 2;k ++ )MS[i][j] = min( MS[i][j], ans[i][k] + matrix[num][k][j] );memcpy( ans, MS, sizeof( MS ) );return;}int mid = ( l + r ) >> 1;if( L <= mid ) query( num << 1, l, mid, L, R );if( mid < R ) query( num << 1 | 1, mid + 1, r, L, R ); }void modify( int x, int t ) {if( t == -1 ) {matrix[rnk[x]][0][0] = inf;matrix[rnk[x]][0][1] = g[x][0];matrix[rnk[x]][1][0] = matrix[rnk[x]][1][1] = g[x][1];}else if( t == 0 ) {//不能駐扎軍隊f[x][1]貢獻必須無 設其轉移矩陣inf 最后取min才會不選matrix[rnk[x]][0][0] = matrix[rnk[x]][1][0] = matrix[rnk[x]][1][1] = inf;matrix[rnk[x]][0][1] = g[x][0];}else {//必須駐扎軍隊f[x][1]貢獻必須有 設f[x][0]轉移矩陣inf 取min一定選f[x][1]matrix[rnk[x]][0][0] = matrix[rnk[x]][0][1] = inf;matrix[rnk[x]][1][0] = matrix[rnk[x]][1][1] = g[x][1];}modify( 1, 1, n, dfn[x] );//線段樹的矩陣更新 while( x ) {//爬鏈x = top[x];ans[0][0] = ans[1][1] = 0;ans[0][1] = ans[1][0] = inf;//廣義矩陣下的單位矩陣定義query( 1, 1, n, Left[x], Right[x] );//查詢整條重鏈′ int IS = f[x][0], SI = f[x][1];f[x][0] = min( ans[0][0], ans[0][1] );f[x][1] = min( ans[1][0], ans[1][1] );if( x == 1 ) return;int y = fa[x];//輕鏈跳到重鏈上g[y][0] += ( f[x][1] - SI );//更新y(不選)的輕兒子貢獻 減去原來的加上現在的f[x][1]g[y][1] += ( min( f[x][0], f[x][1] ) - min( SI, IS ) );//更新y(選)的輕兒子貢獻減去原來的min加上現在的min(f[x][1],f[x][0])matrix[rnk[y]][0][1] = g[y][0];matrix[rnk[y]][1][0] = matrix[rnk[y]][1][1] = g[y][1];modify( 1, 1, n, dfn[y] );x = y;} }signed main() {freopen( "defense.in", "r", stdin );freopen( "defense.out", "w", stdout );scanf( "%lld %lld %s", &n, &m, type );for( int i = 1;i <= n;i ++ )scanf( "%lld", &p[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 );build( 1, 1, n );while( m -- ) {int a, x, b, y;scanf( "%lld %lld %lld %lld", &a, &x, &b, &y );if( dep[a] < dep[b] ) swap( a, b ), swap( x, y ); //先修改上面的點的話 后面在下面的點的修改其實會影響上面的點 那么之前上面的點的轉移就是虛假的modify( a, x ), modify( b, y );if( min( f[1][0], f[1][1] ) >= inf ) printf( "-1\n" );else printf( "%lld\n", min( f[1][0], f[1][1] ) );modify( a, -1 ), modify( b, -1 );//詢問獨立最后要重置矩陣}return 0; }總結
以上是生活随笔為你收集整理的线性代数四之动态DP(广义矩阵加速)——Can you answer these queries III,保卫王国的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线性代数三之状压DP的矩阵加速——Qua
- 下一篇: [数论系列一]C Looooops,跳跳