矩阵相关

[abcd]

矩阵乘法

基础部分

e.g.

[a1,1a1,2a2,1a2,2][b1,1b1,2b2,1b2,2]=[a1,1b1,1+a1,2b2,1a1,1b1,2+a1,2b2,2a2,1b2,1+a2,2b2,1a2,1b1,2+a2,2b2,2]

规则

第一个矩阵的列数必须等于第二个矩阵的行数。

运算律

  1. A×BB×A

 A=[a1a2an],B=[b1b2bn]A ×B=[a1b1a1b2a1bna2b1a2b2a2bnanb1anb2anbn]B ×A=[a1b1+a2b2+anan]

  1. A×B×C=A×(B×C)

    这条性质也是矩阵快速幂的前提

O(n) 的程序通过矩阵优化转化至矩阵快速幂,变成 O(k3logn) 的复杂度(k 是矩阵大小)。

例题——矩阵加速数列生成


P1962 斐波那契数列

[1110]×[FnFn1]=[Fn+Fn1Fn]

ANS=[11] [1110]n2×ANS=[FnFn1]

P2044 [NOI2012] 随机数生成器

[Xnc00]×[a011]=[aXn+cc00]

矩阵快速幂优化 dp

P5343【XR-1】分块

令可行的块长为 a1,a2,,am,则转移方程显然:

fi=j=1mfiaj

但是显然这个复杂度是不可接受的,所以我们考虑优化。

考虑到块长的范围只有 100,可以利用矩阵快速幂加速解决本题。

x 表示可行的块长中最长的

设状态矩阵为

[fxfx1fx2f3f2f1]

要转移到

[fx+1fxfx1f4f3f2]

我们要构造一个 m×m 的矩阵来转移

首先,也是一般矩阵加速 dp 的相通之处,观察到 f2fx 这部分是不变的,那么我们可以先向转移矩阵内填入:

[100000010000000000000100000010]

思考第一行填入什么,观察状态转移方程得,fx+1 应该由 fx+1a1,fx+1a2,,fx+2am 转移而来,所以矩阵的第一行显而易见是:

ai 处填 1,其余地方填 0

可得

[100000010000000000000100000010]nm+1×[fx1fx2fx3f2f1f0]=[fnfn1fn2fnm+3fnm+2fnm+1]

快速幂转移即可,最终答案是答案矩阵的最上方

记得开 long long

P3758 [TJOI2017]可乐

考虑一个更一般的情况,给定一张 n 个点无边权有向图,问从 s 经过 k 条边走向 t 的方案总数?

设矩阵 A 是这张图的邻接矩阵,则 Akai,j 表示 ij 经过 k 条边的行进路线种数,理解见下:

B=ApC=AqR=B×C=Ap+q,则根据矩阵乘法的定义,

Ri,j=k=1nAk,jBi,k=k=1nBi,kAk,j

显然结论是正确的。可以通过矩阵快速幂加速。

再回到这道题上,在原地停留显然可以自环,而自爆可以看作连一条通向 n+1 的单向边,也就是说去了另一个世界(雾

代码就十分简单了。

P3977 [TJOI2015]棋盘

这篇文章是给像我一样初学矩阵优化 dp,然后看其他大佬写的题解看的一脸雾水的蒟蒻看的,所以比较详细,语文水平不好还请多多包涵


先用状压 dp 的思路分析,每一行的摆放状态可以压成一个二进制数,根据给出矩阵的第二行可以预处理出每一行合法的状态集合 S,再 dfs / 瞎搞出相邻行的合法转移,设 fi,sta 表示第 i 行的状态是 sta 时的方案总数,checksta,nxt 表示上一行的状态为 sta,下一行的状态为 nxt 是否合法,则转移方程显然:

fi,sta=preScheckpre,stafi1,pre

问题是,这样转移的时间复杂度为 O(n22m),大概是在 4e9 的级别,无法接受。

注意到每一层的转移都是相同的,于是我们可以考虑用矩阵快速幂优化。

为了方便描述,我们将 S 构成的序列称作 p1,p2,,psize,设矩阵 Akai,j 表示状态 ai 经过 k 行转移到状态 aj 的方案总数,则初始矩阵为

ai,j=checkpi,pj

再来看转移矩阵,考虑等式 Ap×Aq=Ap+q,令 B=AqC=Ap+q,则我们想做到 ci,j=k=1sizeai,kbk,j,注意到这个式子恰好符合矩阵乘法的定义,所以转移矩阵与初始矩阵相同,接下来矩阵快速幂即可。

注意到第 1 行到第 n 行转移了 n1 次,所以指数要等于 n1

Code

#include<bits/stdc++.h>
#define int unsigned int
const int maxn = 1000006, maxm = 6;
int n, m, p, K, len, siz;
int att[3];
std :: vector<int> fst;
int cal(int x, int p, int ik) {
    if(p <= ik) 
        return x << (ik - p);
    return x >> (p - ik);
}
bool check(int x) {
    int tmp = x;
    for(int i = 0; tmp >> i; ++ i) {
        if((x & (1 << i)) == 0) continue;
        if((x & cal(att[1], p, i + K)) & ((len - 1) ^ (1 << i))) 
            return false;
    }
    return true;
}//一行内是否合法
bool con(int x, int y) {
    x ^= y ^= x ^= y;
    int tmp = x;
    for(int i = 0; i < m; ++ i) {
        if((x & (1 << i)) == 0) continue;
        if(y & cal(att[2], p, i + K)) 
            return false;
    }     
    tmp = y;
    for(int i = 0; i < m; ++ i) {
        if((y & (1 << i)) == 0) continue;
        if(x & cal(att[0], p, i + K))
            return false;
    }    
    return true;
}//相邻两行是否合法
struct matrix {
    int m[(1 << maxm) + 5][(1 << maxm) + 5], a, b;
    matrix (int x, int y) {
        a = x; b = y;
        for(int i = 1; i <= a; ++ i)
            for(int j = 1; j <= b; ++ j)
                m[i][j] = 0;
    }
    matrix friend operator *  (matrix x, matrix y) {
        matrix ans(x.a, y.b);
        for(int i = 1; i <= x.a; ++ i)
            for(int k = 1; k <= y.a; ++ k) {
                int s = x.m[i][k];
                for(int j = 1; j <= y.b; ++ j)
                    ans.m[i][j] = (ans.m[i][j] + s * y.m[k][j]);
            }
        return ans;
    }
};
matrix ksm(matrix base, int p) {
    matrix res(siz, siz);
    for(int i = 1; i <= siz; ++ i) res.m[i][i] = 1;
    while(p) {
        if(p & 1) res = base * res;
        base = base * base; p >>= 1;
    }
    return res;
}   
signed main() {
    std :: cin >> n >> m >> p >> K; ++ K;
    len = 1 << m;
    for(int i = 0; i < 3; ++ i)
        for(int j = 1, tmp; j <= p; ++ j)
            att[i] <<= 1, std :: cin >> tmp, att[i] |= tmp;
    fst.push_back(0x7fffffff);
    for(int i = 0; i < len; ++ i)
        if(check(i)) fst.push_back(i);
    siz = fst.size() - 1; 
    matrix zy(siz, siz);
    for(int i = 1; i <= siz; ++ i) 
        for(int j = 1; j <= siz; ++ j)
            if(con(fst[i], fst[j]))
                zy.m[i][j] = 1;
    matrix res = ksm(zy, n - 1);
    int ans = 0;
    for(int i = 1; i <= siz; ++ i) {
        for(int j = 1; j <= siz; ++ j)
            ans += res.m[i][j];
    }
    std :: cout << ans;
    return 0;
}

P3216 [HNOI2011]数学作业

fi1i 连起来得到的数,则显然可以得到:fi=fi1×cnt(i)+i,其中 cnt(i) 表示 i 的位数,可以发现对于位数相同的数来说,这个转移方程是固定的,于是考虑用矩阵优化:对于位数为 k 的数,有如下转移:

[10k11011001]×[fii1]=[fi+1i+11]

分段转移即可。

posted @   8ovehat1r  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
点击右上角即可分享
微信分享提示