题意

​ 题目链接:https://www.luogu.org/problem/P4150

​ 一个 \(6\times n\) 的网格图,每个格点有一个初始权值。有两种操作:

  1. 修改一个格子的权值

  2. 求两个格子之间的最短路的权值。

​ \(1 \leq n \leq 10^5\)

引言

​ 显然这种题目肯定是要用线段树了,对于每一个线段树区间,我们考虑开三个 \(6\times 6\) 的数组,分别表示从左边第 \(i\) 行走到左边第 \(j\) 行、右边第 \(i\) 行走到右边第 \(j\) 行、左边第 \(i\) 行走到右边第 \(j\) 行的最小代价。这个转移是从 \((i,j),(j,k)\) 转移到 \((i,k)\) 的,和矩乘的形式有点像。首先,我们总结一下不同形式的矩阵。

正文

乘加矩阵

​ 数字排成一个 \(n\) 行 \(m\) 列的数阵被称为矩阵,我们在若干计数问题中已经遇到过一类常见的乘加矩阵,一般可以定义如下的运算:

运算

  • 加法运算

    ​ 设 \(A,B,C\) 是三个 \(n\) 行 \(m\) 列的矩阵,若 \(C=A+B\) ,则 \(C_{i,j}=A_{i,j}+B_{i,j}\)。

  • 乘法运算

    ​ 设 \(A\) 为 \(n\) 行 \(p\) 列的矩阵, \(B\) 为 \(p\) 行 \(m\) 列的矩阵, \(C\) 为 \(n\) 行 \(m\) 列的矩阵,若 \(C = AB\) ,则 \(\displaystyle C_{i,j}=\sum_{k=1}^pA_{i,k}B_{k,j}\) 。

性质

​ 我们不难发现这样定义运算,乘加矩阵是满足结合律和分配律,不满足交换律的(下文将给出统一证明)。

加min矩阵

​ 我们可以将乘加矩阵的乘法换成加法,把加法换成求 \(\min\) ,就可以类似的定义出一类新的矩阵,加\(\min\)矩阵,运算定义如下:

运算

  • “加法”运算

    ​ 设 \(A,B,C\) 是三个 \(n\) 行 \(m\) 列的矩阵,若 \(C=A+B\) ,则 \(C_{i,j}=\min\{A_{i,j},B_{i,j}\}\)。

  • “乘法”运算

    ​ 设 \(A\) 为 \(n\) 行 \(p\) 列的矩阵, \(B\) 为 \(p\) 行 \(m\) 列的矩阵, \(C\) 为 \(n\) 行 \(m\) 列的矩阵,若 \(C = AB\) ,则 \(\displaystyle C_{i,j}=\min_{k=1}^p\{A_{i,k}+B_{k,j}\}\) 。

​ 这类矩阵在转移是加\(\min\)形式的时候有奇效。

性质

​ 加\(\min\)矩阵也是满足结合律和分配律,不满足交换律的。

矩阵的统一性质

矩阵统一定义

​ 规定“乘法”和“加法”含义之后,就可以重新定义出一类矩阵,我们设元素 \(x,y\) 加法运算为 \(f(x,y)\) ,元素 \(x,y\) 的乘法运算为 \(g(x,y)\) ,在此意义下,元素的累和为 \(F_{i=lower}^{upper}\) 。

​ 我们可以由此得到新定义矩阵的加法和乘法:

  • “加法”运算

    ​ 设 \(A,B,C\) 是三个 \(n\) 行 \(m\) 列的矩阵,若 \(C=A+B\) ,则 \(C_{i,j}=f(A_{i,j},B_{i,j})\)。

  • “乘法”运算

    ​ 设 \(A\) 为 \(n\) 行 \(p\) 列的矩阵, \(B\) 为 \(p\) 行 \(m\) 列的矩阵, \(C\) 为 \(n\) 行 \(m\) 列的矩阵,若 \(C = AB\) ,则 \(\displaystyle C_{i,j}=F_{k=1}^pg(A_{i,k},B_{k,j})\) 。

结论

​ 观察上面两类常见矩阵的共同点,我们可以得到一个比较普遍的结论:

如果新定义的“乘法”对新定义的“加法”满足分配律的话,那么由该乘法和加法定义出的矩阵满足结合律和分配律。

​ 形式的说, \(\displaystyle g(x,F_{i=lower}^{upper}y_i)=\displaystyle F_{i=lower}^{upper}g(x,y_i)\) 。

​ 实际上,我们常说的分配律也就是指提取 \(\displaystyle\sum\) 的性质,可能说的不大清楚,我们举乘加矩阵和加\(\min\)矩阵为例(下文再证明该结论):

  1. (乘加矩阵)由于 \(\displaystyle x\sum_{i}y_i=\displaystyle \sum_{i}xy_i\) ,那么由数学上我们通常说的乘法和加法定义出的矩阵是满足结合律和分配律的。
  2. (加\(\min\)矩阵)由于 \(\displaystyle x+\min_{i}\{y_i\}= \min_{i}\{x+y_i\}\) ,那么由加法和 \(\min\) 运算定义出来的矩阵也是满足结合律和分配律的。
  3. (我也不知道有没有这种矩阵)由于 \(\displaystyle x+\prod_{i}y_i\neq\displaystyle \prod_{i}(x+y_i)\) ,所以把数学上的乘法当成元素的加法,把数学上的加法当成元素的乘法,如此定义出的矩阵不满足结合律和分配律(这个矩阵的加法为 \(\displaystyle C_{i,j}=A_{i,j}B_{i,j}\) ,乘法为 \(\displaystyle C_{i,j}=\prod_{k=1}^p(A_{i,k}+B_{k,j})\) ,随便举了一个例子,为了说明一下结论的普适性)。

​ 该结论可以作为判断新定义出的矩阵是否满足结合律与分配律的条件。

​ 为了方便起见,我们还是令 \(+\) 表示矩阵元素的“加法”运算, \(\times\) 表示矩阵元素的“乘法”运算。类似的, \(\displaystyle\sum\) 表示在新定义的加法下的累和。令新定义出的“加法”和“乘法”满足上述的分配律,我们来证明以此定义出的矩阵满足结合律和分配律。

结合律

​ 设 \(A\) 为 \(n\) 行 \(p\) 列的矩阵, \(B\) 为 \(p\) 行 \(q\) 列的矩阵, \(C\) 为 \(q\) 行 \(m\) 列的矩阵,有 \((AB)C=A(BC)\) 。

证明:

\[\begin{align*}
[(AB)C]_{i,j}&=\sum_{l=1}^q(AB)_{i,l}C_{l,j}\\
&=\sum_{l=1}^q(\sum_{k=1}^pA_{i,k}B_{k,l})C_{l,j}\\
&=\sum_{k=1}^pA_{i,k}(\sum_{l=1}^qB_{k,l}C_{l,j})\\
&=\sum_{k=1}^pA_{i,k}(BC)_{k,j}\\
&=[A(BC)]_{i,j}
\end{align*}
\]

得证,需要用到分配律提取 \(\displaystyle \sum\) 。

分配律

​ 设 \(A\) 为 \(n\) 行 \(p\) 列的矩阵, \(B^{[1]}\) 到 \(B^{[c]}\) 均为 \(p\) 行 \(m\) 列的矩阵,有 \(\displaystyle A\sum_{a=1}^cB^{[i]}=\sum_{a=1}^cAB^{[i]}\) 。

证明:

\[\begin{align*}
(A\sum_{a=1}^cB)_{i,j}&=\sum_{k=1}^pA_{i,k}{\big(}\sum_{a=1}^c B^{[a]}{\big)}_{k,j}\\
&=\sum_{k=1}^pA_{i,k}{\big(}\sum_{a=1}^cB^{[a]}_{k,j}{\big)}\\
&=\sum_{a=1}^c\sum_{k=1}^pA_{i,k}B^{[a]}_{k,j}\\
&=\sum_{a=1}^c(AB^{[a]})_{i,j}\\
&=(\sum_{a=1}^cAB^{[a]})_{i,j}
\end{align*}
\]

得证,同样需要用到分配律提取 \(\displaystyle \sum\) 。

​ 到此为止,我们已经证明了乘加矩阵,加\(\min\)矩阵,以及对乘加可分配的矩阵存在结合律和分配律。而矩阵的优化都依赖于结合律和分配律,比如常见的矩阵快速幂,预处理矩阵乘积等技巧,没有这两个定律的支撑都是一纸空谈。

本题思路

​ 回到本题,我们发现每个线段树节点上的 \(6\times 6\) 的数组其实就是一个 \(6\times 6\) 的加\(\min\)矩阵。两个线段树节点合并的函数可以写的非常简单:

node operator  + (const node &_)const
{
static node res;
res.A = A + C * _.A * (~C);
res.B = _.B + (~_.C) * B * _.C;
res.C = C * (MT1 + _.A * B) * _.C;
return res;
}

​ 这里的取反符号表示矩阵行列翻转,在本题里表示从左边走到右边翻转成从右边走到左边。

​ \(MT1\) 表示单位 \(1\) 矩阵,即乘上去不变的矩阵,加\(\min\)矩阵的单位 \(1\) 矩阵大概长成这个样子:

\[\begin{pmatrix}
0&\infty&\infty&\infty&\infty&\cdots\\
\infty&0&\infty&\infty&\infty&\cdots\\
\infty&\infty&0&\infty&\infty&\cdots\\
\infty&\infty&\infty&0&\infty&\cdots\\
\infty&\infty&\infty&\infty&0&\cdots\\
\vdots&\vdots&\vdots&\vdots&\vdots&\ddots
\end{pmatrix}
\]

​ 比较好理解,注意结合矩阵的结合律和分配律。求最优解中的加号对应计数中的加法原理(并列),乘号对应乘法原理(分步)。

​ 而对于询问,设起点在终点左边,称与起点横坐标相同的点形成的线为起点线,与种点横坐标相同的点形成的线为种点线,则可以把过程分成三步:

  1. 先在起点线右边兜一圈回来,再在起点线左边兜一圈回来;

  2. 兜到终点线;

  3. 先在终点线右边兜一圈回来,再在终点线左边兜一圈回来。

​ 注意行数为 \(6\) ,一三步最多只能执行一次,就算可以执行多次,套上矩阵快速幂就行了(感受到矩阵的优美了没有)。

​ 当然一三步可以不走,体现在状态里就是加上一个单位 \(1\) 矩阵,表示这步不执行。

​ 详见代码,矩阵的好处之一就是写在代码中短而优雅。

代码

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define FOR(i, x, y) for(int i = (x), i##END = (y);i <= i##END; ++i)
#define DOR(i, x, y) for(int i = (x), i##END = (y);i >= i##END; --i)
template<typename T, typename _T>inline bool chk_min(T &x, const _T y){return y < x? x = y, 1 : 0;}
template<typename T, typename _T>inline bool chk_max(T &x, const _T y){return x < y? x = y, 1 : 0;}
typedef long long ll;
const int N = 1e5 + 5;
struct Matrix
{
int a[6][6];
Matrix() {asn0();}
void asn0() {FOR(i, 0, 5) FOR(j, 0, 5) a[i][j] = 2e9;}
void asn1() {FOR(i, 0, 5) FOR(j, 0, 5) a[i][j] = 2e9 * (i != j);}
int *operator [](const int x) {return a[x];}
Matrix operator ~() const
{
Matrix res = (*this);
FOR(i, 0, 5) FOR(j, 0, i - 1) std::swap(res.a[i][j], res.a[j][i]);
return res;
}
Matrix operator + (const Matrix _) const
{
Matrix res;
FOR(i, 0, 5) FOR(j, 0, 5) res.a[i][j] = std::min(a[i][j], _.a[i][j]);
return res;
}
Matrix operator *(const Matrix _) const
{
Matrix res;
FOR(i, 0, 5) FOR(j, 0, 5) FOR(k, 0, 5) chk_min(res.a[i][j], a[i][k] + _.a[k][j]);
return res;
}
void operator *=(const Matrix _)
{
(*this) = (*this) * _;
}
}; Matrix MT1; struct node
{
Matrix A, B, C;
void reset(int *a)
{
static int _s[8], *s = _s + 1;
s[-1] = 0; FOR(i, 0, 5) s[i] = s[i - 1] + a[i];
FOR(i, 0, 5) FOR(j, 0, 5)
A[i][j] = B[i][j] = C[i][j] = (i > j ? s[i] - s[j - 1] : s[j] - s[i - 1]);
}
node operator + (const node &_)const
{
static node res;
res.A = A + C * _.A * (~C);
res.B = _.B + (~_.C) * B * _.C;
res.C = C * (MT1 + _.A * B) * _.C;
return res;
}
}; node nd[N << 2];
int a[N][6];
int n, q; void push_up(int k) {nd[k] = nd[k << 1] + nd[k << 1 | 1];}
void build(int k, int a[N][6], int l = 1, int r = n)
{
if(l == r) {nd[k].reset(a[l]); return;}
int mid = (l + r) >> 1;
build(k << 1, a, l, mid);
build(k << 1 | 1, a, mid + 1, r);
push_up(k);
}
void update(int k, int x, int a[6], int l = 1, int r = n)
{
if(l == r) {nd[k].reset(a); return;}
int mid = (l + r) >> 1;
if(x <= mid) update(k << 1, x, a, l, mid);
else update(k << 1 | 1, x, a, mid + 1, r);
push_up(k);
}
node query(int k, int L, int R, int l = 1, int r = n)
{
if(L <= l && r <= R) return nd[k];
int mid = (l + r) >> 1;
if(R <= mid) return query(k << 1, L, R, l, mid);
else if(L > mid) return query(k << 1 | 1, L, R, mid + 1, r);
else return query(k << 1, L, R, l, mid) + query(k << 1 | 1, L, R, mid + 1, r);
} int solve(int l, int a, int r, int b)
{
Matrix L, M, R, LM, MR, res; M = query(1, l, r).C;
LM = query(1, 1, r).B;
MR = query(1, l, n).A; if(l > 1) L = query(1, 1, l - 1).B;
else L = MT1;
if(r < n) R = query(1, r + 1, n).A;
else R = MT1;
res = (MT1 + MR * L) * M * (MT1 + R * LM); return res[a][b];
} int main()
{
MT1.asn1(); scanf("%d", &n);
FOR(j, 0, 5) FOR(i, 1, n)scanf("%d", &a[i][j]); build(1, a); scanf("%d", &q);
while(q--)
{
int cmd, x, y, p, q, c;
scanf("%d", &cmd); if(cmd == 1)
{
scanf("%d%d%d", &x, &y, &c);
x--;
a[y][x] = c;
update(1, y, a[y]);
}
else
{
scanf("%d%d%d%d", &x, &y, &p, &q);
x--, p--;
if(y > q) std::swap(x, p), std::swap(y, q);
printf("%d\n", solve(y, x, q, p));
}
}
return 0;
}

【对不同形式矩阵的总结】WC 2009 最短路径问题(线段树+矩阵乘法)的更多相关文章

  1. Wannafly Winter Camp 2019.Day 8 div1 E.Souls-like Game(线段树 矩阵快速幂)

    题目链接 \(998244353\)写成\(99824435\)然后调这个线段树模板1.5h= = 以后要注意常量啊啊啊 \(Description\) 每个位置有一个\(3\times3\)的矩阵, ...

  2. Z0J 3772 Calculate the Function 线段树+矩阵

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5235 这种题目居然没想到,一开始往矩阵快速幂想去了,因为之前跪了太多矩阵快速幂 ...

  3. [HDU6155]Subsequence Count(线段树+矩阵)

    DP式很容易得到,发现是线性递推形式,于是可以矩阵加速.又由于是区间形式,所以用线段树维护. https://www.cnblogs.com/Miracevin/p/9124511.html 关键在于 ...

  4. CF718C Sasha and Array 线段树 + 矩阵乘法

    有两个操作: 将 $[l,r]$所有数 + $x$ 求 $\sum_{i=l}^{r}fib(i)$ $n=m=10^5$   直接求不好求,改成矩阵乘法的形式:  $a_{i}=M^x\times ...

  5. CF719E(线段树+矩阵快速幂)

    题意:给你一个数列a,a[i]表示斐波那契数列的下标为a[i],求区间对应斐波那契数列数字的和,还要求能够维护对区间内所有下标加d的操作 分析:线段树 线段树的每个节点表示(f[i],f[i-1])这 ...

  6. 【Codeforces718C】Sasha and Array 线段树 + 矩阵乘法

    C. Sasha and Array time limit per test:5 seconds memory limit per test:256 megabytes input:standard ...

  7. Codeforces Round #337 (Div. 2) D. Vika and Segments 线段树 矩阵面积并

    D. Vika and Segments     Vika has an infinite sheet of squared paper. Initially all squares are whit ...

  8. 线段树 + 矩阵 --- ZOJ 3772 Calculate the Function

    Calculate the Function Problem's Link:   http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCod ...

  9. ZOJ3772 - Calculate the Function(线段树+矩阵)

    题目大意 给定一个序列A1 A2 .. AN 和M个查询 每个查询含有两个数 Li 和Ri. 查询定义了一个函数 Fi(x) 在区间 [Li, Ri] ∈ Z. Fi(Li) = ALi Fi(Li ...

随机推荐

  1. Kubernetes Pod 资源限制

    Kubernetes Pod 资源限制 官方文档:https://kubernetes.io/docs/concepts/configuration/manage-compute-resources- ...

  2. Python platform 模块

    Python platform 模块 platform 模块用于查看当前操作系统的信息,来采集系统版本位数计算机类型名称内核等一系列信息. 使用方法: import platform # 获取操作系统 ...

  3. 【转载】百度百科:FusionCube超融合

    [转载]百度百科:FusionCube超融合 华为FusionCube融合基础设施一体机(Huawei FusionCube Converged Infrastructure)是华为公司IT产品线云计 ...

  4. asp.net 创建虚拟目录 iis创建虚拟目录

    这几天本人接了个档案管理查询系统的小项目,踩过的坑. 其实功能都挺简单的,大致要求客户有很多pdf文档,为了方便管理,所有要开发一个相当于文件管理系统,本人正好有现成的文件管理系统,修改下就可以.其中 ...

  5. 钉钉企业内部H5微应用开发

    企业内部H5微应用开发 分为 服务端API和前端API的开发,主要涉及到进入应用免登流程和JSAPI鉴权. JSAPI鉴权开发步骤: 1.创建H5微应用 登入钉钉开放平台(https://open-d ...

  6. Scrum冲刺第一篇

    一.各个成员在 Alpha 阶段认领的任务 负责人和协作者 任务内容 陈嘉欣 设计编码规范 邓镇港 UI设计 肖烈涛 数据库设计 林德泽 设计测试计划 余晓东 用户注册登陆验证模块 陈嘉欣 余晓东 林 ...

  7. 还学不会webpack?看这篇!

    摘要: webpack入门教程. 原文:还学不会webpack?看这篇! 作者:MudOnTire Fundebug经授权转载,版权归原作者所有. Webpack已经流行好久了,但很多同学使用webp ...

  8. element-ui的表单验证this.$refs[formName].validate的代码不执行

    经过排查,如果自定义验证中,每种情况都要写明确和有回调函数callback var validatePhone = (rule, value, callback) => { const reg ...

  9. SQL Server学习内容(一)

    SQL Server SQL Server对大小写不敏感,每条语句末端使用分号. 1.SQL命令 SELECT 从数据中提取数据 UPDATE 更新数据中的数据 DELETE 从数据库中删除数据 IN ...

  10. 155--MinStack

    /* 解法一:使用链表从0实现栈,用min来存放最小值. 复杂的地方是,如果pop了最小的数,就要遍重新找到最小的数. */ public class MinStack { List<Integ ...