矩阵乘法学习记录
這要從校賽的一個區間與非操作題說起,群里大佬用的ddp思想使其滿足結合律,但是我連矩陣乘法都不會于是從頭開始學習矩陣乘法。
P3390 【模板】矩陣快速冪
和快速冪一模一樣,只是把數乘換成矩陣乘,只需要定義結構體矩陣然后重載一下乘法*即可。
注意:
111乘以任何數都等于這個數本身
單位矩陣乘以任何矩陣就等于這個矩陣本身
P1962 斐波那契數列
[0011][fn?2fn?1]=[fn?1fn]→[0011]n?2[f1f2]=[fn?1fn]\begin{bmatrix} 0 & 0 \\ 1&1 \end{bmatrix} \begin{bmatrix} f_{n-2}\\f_{n-1} \end{bmatrix}=\begin{bmatrix} f_{n-1}\\f_{n} \end{bmatrix} \to\begin{bmatrix} 0 & 0 \\ 1&1 \end{bmatrix}^{n-2} \begin{bmatrix} f_{1}\\f_{2} \end{bmatrix}=\begin{bmatrix} f_{n-1}\\f_{n} \end{bmatrix} [01?01?][fn?2?fn?1??]=[fn?1?fn??]→[01?01?]n?2[f1?f2??]=[fn?1?fn??]
矩陣乘法滿足結合律,由此可以根據上述式子進行矩陣快速乘
P1939 【模板】矩陣加速(數列)和上面這個題基本一樣。
P2044 [NOI2012]隨機數生成器
[a101][xn?1c]=[xnc]→[a101]n[x0c]=[xnc]\begin{bmatrix} a & 1 \\ 0&1 \end{bmatrix} \begin{bmatrix} x_{n-1}\\c \end{bmatrix}=\begin{bmatrix} x_{n}\\c \end{bmatrix} \to\begin{bmatrix} a & 1 \\ 0&1 \end{bmatrix}^n \begin{bmatrix} x_{0}\\c \end{bmatrix}=\begin{bmatrix} x_{n}\\c \end{bmatrix}[a0?11?][xn?1?c?]=[xn?c?]→[a0?11?]n[x0?c?]=[xn?c?]
這題比較dt的地方是兩個數相乘會爆long long,于是上龜速乘防止乘積爆
SP1716 GSS3 - Can you answer these queries III
矩陣乘法優化dp
考慮P1115 最大子段和如何做?
不難得知設計dp即可。
狀態表示:fif_ifi?表示以第iii個位置為結尾最大字段和,gi=max(f1,f2,…,fi)g_i=max(f_1,f_2,\dots,f_i)gi?=max(f1?,f2?,…,fi?)
狀態轉移:fi=max(fi?1+ai,ai)f_i=max(f_{i-1}+a_i,a_i)fi?=max(fi?1?+ai?,ai?),gi=max(gi?1,fi)g_i=max(g_{i-1},f_i)gi?=max(gi?1?,fi?)
最終答案即是gng_ngn?
總所周知遞推不滿足結合律,換句話說就是你必須一步一步遞推,不過矩陣乘法能夠優化遞推 斐波那契前 n 項和 ,而優化的方式就是使計算過程具有結合律,那么如果我們通過矩陣操作使一些不具有結合律的東西具有結合律那么我們就能用線段樹維護這個東西,花費log代價顯然使非常優秀的。
效仿矩陣乘法優化斐波那契的方法尋找矩陣
[ai?∞aiai0ai?∞?∞0]?[fn?1gn?10]=[fngn0]\begin{bmatrix} a_i&-\infty&a_i\\a_i&0&a_i\\ -\infty&-\infty&0\\ \end{bmatrix}? \begin{bmatrix} f_{n-1}\\g_{n-1}\\0 \end{bmatrix}= \begin{bmatrix} f_n\\g_n\\0 \end{bmatrix} ???ai?ai??∞??∞0?∞?ai?ai?0????????fn?1?gn?1?0????=???fn?gn?0????
???表示一個運算
[abcd]?[ef]=[max(a+e,b+f)max(c+e,d+f)]\begin{bmatrix} a&b\\c&d \end{bmatrix}? \begin{bmatrix} e\\f \end{bmatrix}=\begin{bmatrix}max(a+e,b+f)\\max(c+e,d+f) \end{bmatrix}[ac?bd?]?[ef?]=[max(a+e,b+f)max(c+e,d+f)?]
不難發現上述定義的新運算是具有結合律的!!!
對比此運算和矩陣乘法與運算不難發現:
矩陣乘法中的“乘”相當于這里的“加”而矩陣乘法中的“加”相當于這里的“max”
矩陣乘法滿足結合律實際上使“乘”對“加”滿足分配了,而這里“加”對“max”同樣滿足分配率于是新運算具有結合律。
滿足結合律并且區間查詢單點修改無疑線段樹,只需要每個節點維護一個矩陣即可。
考慮答案在哪?
定義:
Ai=[ai?∞aiai0ai?∞?∞0]A_i= \begin{bmatrix} a_i&-\infty&a_i\\a_i&0&a_i\\ -\infty&-\infty&0\\ \end{bmatrix}Ai?=???ai?ai??∞??∞0?∞?ai?ai?0????
Bi=A1?A2?…?AiB_i= A_1?A_2?\dots\ ?A_i Bi?=A1??A2??…??Ai?
那么再看此題 P1115 最大子段和,不難得出
Bn?[000]=[fngn0]B_n ?\begin{bmatrix} 0\\0\\0 \end{bmatrix}=\begin{bmatrix} f_n\\g_n\\0 \end{bmatrix}Bn?????000????=???fn?gn?0????
答案就是gng_ngn?,不難發現答案就蘊藏著BnB_nBn?矩陣中,分析一下不難得知如果Bn=[b11b12b12b21b22b23b31b32b33]B_n=\begin{bmatrix} b_{11}&b_{12}&b_{12}\\b_{21}&b_{22}&b_{23}\\ b_{31}&b_{32}&b_{33}\\ \end{bmatrix}Bn?=???b11?b21?b31??b12?b22?b32??b12?b23?b33?????那么答案就是max(b21,b23)max(b_{21},b_{23})max(b21?,b23?)
而本題有了之前的工作只需要套個線段樹就是基本的區間修改單點查詢問題。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int maxn=3,INF=0x3f3f3f3f; const int N=50010;//投機取巧 int n,q,a; int sz;//矩陣大小 struct node {int l,r;int m[maxn][maxn];node(){l=r=0;memset(m,0,sizeof m);};node operator *(const node &b) const{node res;res.l=l,res.r=b.r;memset(res.m,-0x3f,sizeof res.m);for(int i=0;i<sz;i++)for(int j=0;j<sz;j++)for(int k=0;k<sz;k++) res.m[i][j]=max(res.m[i][j],m[i][k]+b.m[k][j]);return res;}int ans(){return max(m[1][0],m[1][2]);} }tree[N<<2]; void pushup(int u) {tree[u]=tree[u<<1]*tree[u<<1|1]; } void build(int u,int l,int r) {if(l==r){cin>>a;//這樣輸入省空間tree[u].l=tree[u].r=r;tree[u].m[0][0]=tree[u].m[1][0]=tree[u].m[0][2]=tree[u].m[1][2]=a;tree[u].m[0][1]=tree[u].m[2][0]=tree[u].m[2][1]=-INF;return;}int mid=l+r>>1;build(u<<1,l,mid);build(u<<1|1,mid+1,r);pushup(u); } void modify(int u,int pos,int x) {if(tree[u].l==tree[u].r){tree[u].m[0][0]=tree[u].m[1][0]=tree[u].m[0][2]=tree[u].m[1][2]=x;return;}int mid=tree[u].l+tree[u].r>>1;if(pos<=mid) modify(u<<1,pos,x);else modify(u<<1|1,pos,x);pushup(u); } node query(int u,int l,int r) {if(tree[u].l>=l&&tree[u].r<=r) return tree[u];int mid=tree[u].l+tree[u].r>>1;if(r<=mid) return query(u<<1,l,r);else if(l>mid)return query(u<<1|1,l,r);else return query(u<<1,l,r)*query(u<<1|1,l,r); } int main() {IO;cin>>n;sz=3;build(1,1,n);cin>>q;while(q--){int op,x,y;cin>>op>>x>>y;if(op==1){if(x>y) swap(x,y);cout<<query(1,x,y).ans()<<'\n';}elsemodify(1,x,y);}return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
- 上一篇: 泰勒及洛朗展开学习笔记
- 下一篇: 松糕鞋怎么搭配