P6772 [NOI2020] 美食家 题解(矩阵加速图上dp常用思路)
P6772 [NOI2020] 美食家 题解(矩阵加速图上dp常用思路)
简要题面#
给定一张
每经过一个点
求最大贡献
#
考虑朴素
则我们定义:
然后特判
这种朴素 dp 没有任何问题,满足无后效性,时间复杂度为:
#
看到
矩阵快速幂,矩阵快速幂优化 dp !!
我们现在有了个这么思路,考虑怎么做,现在有两个问题:
- Q1:矩阵快速幂一般是一天天转移的,这个是几天几天转移的,不行!
- Q2:矩阵快速幂一般是边权,这个是点权,不行!
现在我们来思考怎么解决:
- 假 A1:
考虑将这个点到另外一个点的时间延长,变成一个一个,我们就会想到:将一个边拆成
也就是把:
我们看下
然后你算一下点有多少个:
- 真 A1:
拆边不行,这个点比边小多了,我们尝试拆点
如果刚才拆边可以理解为:放慢自己的移动速度,那我们拆点可以理解为:我们在这个点待
怎么做到?我们考虑吧一个点拆成
这样就可以完美的解决了这个问题,一共有
- A2:
你发现,你刚才已经做过了,也就是成功把点权变成了边权,这里就不说了
到这里我们再来考虑怎么矩阵优化:
我们再一次写出状态转移方程:
到了这里,就是一个裸的模版了,我们把
然后我们左边就是枚举每个
没关系,我们重新定义
定义广义矩阵乘法
时间复杂度为:
#
考虑原问题是一整段区间即:
我们考虑怎么加上额外的贡献
很简单,我们在他前一天停下来,加到矩阵里面,继续做就行
最后判一下有没有到终点就行了
这样做时间复杂度为:
#
我们发现,只要在优化一个
我们发现,我们一直在乘
其实是可以的,这里用到了一个常用思想:二进制分解
我们考虑现在是:
然后这里就有个思路,我们预处理
预处理是
Code:#
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m, T, k;
const int MAXN = 2.5e2 + 7, Inf = 1e18 + 7;
int C[MAXN], Id[MAXN][10], Id_cnt;
struct Matrix {
int a[MAXN][MAXN], n, m;
Matrix (int n = 0, int m = 0) : n(n), m(m) {
for (int i = 1; i <= MAXN - 7; i ++)
for (int j = 1; j <= MAXN - 7; j ++)
a[i][j] = -Inf;
}
Matrix operator * (const Matrix b) {
Matrix c = Matrix{n, b.m};
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= b.m; j ++)
for (int k = 1; k <= m; k++)
if (a[i][k] != -Inf && b.a[k][j] != -Inf)
c.a[i][j] = max(a[i][k] + b.a[k][j], c.a[i][j]);
return c;
}
void print() {
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
if (a[i][j] == -Inf) cout << "I" << (j == m ? '\n' : ' ');
else cout << a[i][j] << (j == m ? '\n' : ' ');
}
}E, G, A, K[50];
Matrix qpow(Matrix a, int b) {
Matrix ans = E;
while (b) {
if (b & 1) ans = ans * a;
a = a* a; b >>= 1;
}
return ans;
}
void init() {
E.n = E.m = 5 * n; for (int i = 1; i <= n; i ++) E.a[i][i] = 1;
A.n = 1, A.m = 5 * n; A.a[1][Id[1][1]] = C[1];
K[0] = G;
for (int i = 1; i <= 30; i ++)
K[i] = K[i - 1] * K[i - 1];
}
struct Node {
int t, x, y;
bool operator < (const Node other) const {
return t < other.t;
}
} Ev[MAXN];
void mul(Matrix &A, int b) {
for (int i = 0; i <= 30; i ++) {
if (b & (1 << i)) {
A = A * K[i];
}
}
}
signed main () {
cin >> n >> m >> T >> k;
for (int i = 1; i <= n; i ++) cin >> C[i];
G = Matrix{5 * n, 5 * n};
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= 5; j ++) {
Id[i][j] = ++ Id_cnt;
}
for (int j = 1; j < 5; j ++) {
G.a[Id[i][j]][Id[i][j + 1]] = 0;
}
}
for (int i = 1, u, v, w; i <= m; i ++) {
cin >> u >> v >> w;
G.a[Id[u][w]][Id[v][1]] = C[v];
}
for (int i = 1; i <= k; i ++)
cin >> Ev[i].t >> Ev[i].x >> Ev[i].y;
sort(Ev + 1, Ev + 1 + k);
init();
int last = 0;
for (int i = 1; i <= k; i ++) {
int pw = Ev[i].t - last;
mul(A, pw);
if (A.a[1][Id[Ev[i].x][1]] != -Inf) {
A.a[1][Id[Ev[i].x][1]] += Ev[i].y;
}
last = Ev[i].t;
}
if (last != T) mul(A, T - last);
if (A.a[1][Id[1][1]] == -Inf) cout << -1 << '\n';
else cout << A.a[1][Id[1][1]] << '\n';
return 0;
}
完结撒花✿✿ヽ(°▽°)ノ✿
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现