Phrvth量子力学对不起,爱因斯坦

P6772 [NOI2020] 美食家 题解(矩阵加速图上dp常用思路)

Hi,Phrvth·2023-08-19 00:01·48 次阅读

P6772 [NOI2020] 美食家 题解(矩阵加速图上dp常用思路)

P6772 [NOI2020] 美食家 题解(矩阵加速图上dp常用思路)

简要题面#

给定一张 n 个点 m 条单向边的图,走这条边需要花费 wi 的时间(以天为单位),现在有一个人从 1 号点出发,最后回到 1 号点,要求走了 恰好T 天。

每经过一个点 i,可以获得 Ci 的贡献,最后规定有 k 个特殊点,在走到那恰好Ti 天 时,且恰好Xi 时,可以获得 Yi额外贡献

求最大贡献

1n50m501,0k200,1Ti,T109,1w5

Solution.1:40pts#

考虑朴素 dp,我们发现,如果用天数作为主状态进行转移,这种定义是满足无后效性的,除此之外,我们定义另外一维表示现在是第几个点辅助我们转移。

则我们定义:dpi,j 表示第 i 天走到 j 点时可以获得的最大贡献,我们写出状态转移方程:

dpi,j=maxpjGdpiWp,p+Cj

然后特判 k 个特殊点的情况即可。

这种朴素 dp 没有任何问题,满足无后效性,时间复杂度为:O(T(n+m)),可以通过 40 分!

Solution.2:65pts#

看到 T 这么大,这个时候千万不要慌!思考一下什么东西可以干这么一个事情

矩阵快速幂,矩阵快速幂优化 dp !!

我们现在有了个这么思路,考虑怎么做,现在有两个问题:

  • Q1:矩阵快速幂一般是一天天转移的,这个是几天几天转移的,不行!
  • Q2:矩阵快速幂一般是边权,这个是点权,不行!

现在我们来思考怎么解决:

  • A1:

考虑将这个点到另外一个点的时间延长,变成一个一个,我们就会想到:将一个边拆成 w 条边,然后把这个 w 附到最后一条边上,表示这段路终于走完啦

也就是把:(u,v,w)(u,v1,0),(v1,v2,0)......(vw1,v,w),这样就好了

我们看下 w 的范围,诶 1w5,这绝对是正解!

然后你算一下点有多少个:n+4m,emm,这 m 似乎有点大,额这(

  • A1:

拆边不行,这个点比边小多了,我们尝试拆点

如果刚才拆边可以理解为:放慢自己的移动速度,那我们拆点可以理解为:我们在这个点待 w1 天,然后再瞬移过去!

怎么做到?我们考虑吧一个点拆成 5 个点,也就是 (u,v,w)(u,u1,0),(u1,u2,0).....(uw,v,w)

这样就可以完美的解决了这个问题,一共有 (5n)3 个点

  • A2:

你发现,你刚才已经做过了,也就是成功把点权变成了边权,这里就不说了

到这里我们再来考虑怎么矩阵优化:

我们再一次写出状态转移方程:

dpi,j=maxpjGdpi1,p+Gp,j

到了这里,就是一个裸的模版了,我们把 dpi 看成一个矩阵,其实是个 1×5n 的长条,然后右边是领接矩阵

然后我们左边就是枚举每个 p 的过程,然后右边从上往下固定 j 去算 Gp,j,即可,但是我们发现这里的矩阵乘法和我们的不一样

没关系,我们重新定义

定义广义矩阵乘法 C=ABCi,j=maxk=1nAi,k+Bk,j,并且这个东西满足结合律,证明作者太菜了不会,谁会了踢我一下

时间复杂度为:O((5n)3logT),可以通过 k=0 的部分,结合之前的暴力分即可

Solution.3:75pts#

考虑原问题是一整段区间即:[1,T],我们有 k 个点分割了他们,我们考虑一段一段进行计算,也就是先计算 [1,X1] 在计算 [X1+1,X2],以此类推

我们考虑怎么加上额外的贡献

很简单,我们在他前一天停下来,加到矩阵里面,继续做就行

最后判一下有没有到终点就行了

这样做时间复杂度为:O(klogT(5n)3),可以得到 75pts 的好成绩

Solution.4:100pts#

我们发现,只要在优化一个 n 差不多就行了,考虑矩阵快速幂的时间

我们发现,我们一直在乘 5n×5n 的矩阵,那个长条一直没管他,我们能不能乘多几次长条,不乘那个大矩阵呢?

其实是可以的,这里用到了一个常用思想:二进制分解

我们考虑现在是:AGp,然后我们把 p 做二进制分解假设成为 p1pn,然后答案就是:AGp1Gp2

然后这里就有个思路,我们预处理 G2i,然后用上面的方法做就行了

预处理是 O((5n)3logT),总时间复杂度为:O((5n)3logT+k(5n)2logT),可以通过这题

Code:#

Copy
#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; }

完结撒花✿✿ヽ(°▽°)ノ✿

posted @   Phrvth  阅读(48)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
目录
?