Description

给定一张有向图,\(n\) 个顶点,\(m\) 条边。第 \(i\) 条边从 \(u_i\) 到 \(v_i\),走完该边的用时为 \(w_i\)。每一个点有一个价值 \(c\),走到点 \(i\) 可以得到 \(c_i\) 的价值。

初始时间为 \(0\),你需要从起点 \(1\) 开始,走出一个回到 \(1\) 的有向环,耗时恰好为 \(T\)。最终得到的价值为所有经过的点的价值和。注意这里的环可以经过同个顶点多次,价值和也会被计算多次。

现在有 \(k\) 个附加元素,第 \(i\) 个附加元素有三个参数:\((t_i, x_i, y_i)\)。表示当恰好在 \(t_i\) 时间点到达顶点 \(x_i\) 时,可以得到 \(y_i\) 的额外的价值。

求最大的最终价值和。

Hint

  • \(1\le n\le 50\)
  • \(n\le m\le 501\)
  • \(0\le k\le 200\)
  • \(1\le t_i\le T\le 10^9\)
  • \(1\le w_i\le 5\)
  • \(1\le c_i\le 52501\)
  • \(1\le y_i\le 10^9\)

Solution

首先有一个简单朴素的动态规划:\(f(t, x)\) 表示在 \(t\) 时间点,走到顶点 \(x\) 时,可以得到的最大价值和。显然有:

\[f(t, x) = \max\limits_{(y, x, w) \in \text E} \{f(t-w, y)\} + c_x + g(t, x)
\]

其中 \(g(t, x)\) 表示 \(t\) 时间顶点 \(x\) 的附加值(美食节),如果没有就是 \(0\)。

这个做法是 \(O(m\times T)\) 的,期望 \(40\ \text{pts}\)。


考虑优化。为了方便,\(k\) 个额外的我们会在最后讨论,以下都假设 \(k=0\)。

我们发现,如果 \(w_i = 1\),那么所有边的转移都是“一步到位”的,不存在走了一天还在路上这种东西。那么这样显然可以 矩阵乘法 优化。

具体的,就是假设矩阵 \(B_t = \begin{bmatrix}f(t, 1)\\f(t, 2)\\ \vdots \\ f(t, n) \end{bmatrix}\),即时间点 \(t\) 下的所有状态。我们需要构造一个转移矩阵 \(A\),使得 \(B_{t+1} = A\circ B_t\)。其中 \(\circ\) 为自定义的一种“广义矩阵乘法”,计算公式即为上述状态转移方程:\(c_{i, j} = \max_{1\le k\le n} \{a_{i, k} + b_{k, j}\}\)。可以证明这样定义一定满足结合律。(\(A^x = A\circ A\circ \cdots \circ A\),\(x\) 个 \(A\))

不难发现转移的条件是边存在,于是 \(A\) 就是 反图 (带上价值)的邻接矩阵。为什么是反图?因为这里 \(A_{u, v}\) 的含义是 \(u\) 从 \(v\) 转移而来得到的价值 \(c_u\),并不是 \(u\to v\) 转移过去。

那么直接矩阵快速幂即可,\(B_t = A^t \circ B_0\),答案即为 \((B_t)_{1, 1}\) 的值,复杂度 \(O(n^3\log T)\)。


然而边长 \(w\) 并不是一,为了不让我们前面的努力白费, 我们尝试着将原图的边权“变成”\(1\)。本题中有一个突破口:\(w\le 5\),那么我们的机会来了。

拆边?我们发现点数的规模会达到 \(O(mw+n)\),不可承受。所以选择拆点。

我们把点 \(u\) 拆成五个:\(u_1, u_2, u_3, u_4, u_5\),然后连边 \(u_1\to u_2\to u_3\to u_4\to u_5\),边权都为 \(0\),边长为 \(1\),表示这些拆出来的边不会对答案有贡献。更新转移矩阵 \(A_{u_w, u_{w-1}} = 0\)。

如果原图中有一条边 \((u, v, w)\),那么我们实际的连边为 \(u_w \to v_1\)。不难发现,长度为 \(w\) 的边正好被分为了 \(w\) 段长度为 \(1\) 的边。然后更新转移矩阵 \(A_{v, u_{w}} = c_u\)。

最后得到了一个 \((nw)^2\) 大小的矩阵。

那么现在有了一个应付 \(k=0\) 的数据的做法。


考虑如何加入附加元素(美食节)的影响。发现整个时间段 \([0, T]\) 被时间点 \(t_1, t_2, \cdots , t_k\) 分为了一些段,那我们不如 一段段 地做。

具体的,当 \(t_{i-1}\) 刚做好时,我们直接跳到 \(t_i\),然后在点 \(x_i\) 的对应值加上 \(y_i\)。于是问题被完美地解决了……

显然还没有。这里有两个问题:

  • \(x_i\) 加到那个点上?
  • 复杂度好像有点炸……

下面给出解决方案:

  • 由于我们进行了拆点操作,点 \(x_i\) 应对于 \(5\) 个点中的哪一个呢?假设有一条 \(u_1 \to u_4 \to v_1\) 的路径,其对应的原图的边其实是 \((u, v, 4)\)。对于点 \(u_4\) 而言,它的实际含义其实是“在路上”。对于 \(u_2, u_3, u_5\) 同理。可见只有点 \(u_1\) 才是真正原图上的顶点。于是一个把 \(y_i\) 加在 \((x_i)_1\) 的位置上。
  • 算一下复杂度?我们最多有 \(k\) 次,每次 \(\log (t_i - t_{i-1})\) 次矩阵(方阵×方阵)乘法,总复杂度为 \(O((nw)^3\sum \log (t_i - t_{i-1}))\)。也就是说 我们可能会做 \(200\) 多次矩阵快速幂! 这里会用到一个神奇优化,详情见下:

我们预处理出 \(A\) 之后,再计算一个 \(P\)。其中 \(P_i = A^{2^i}\)。显然 \(P_i = P_{i-1}^2\),可以在 \(O((nw)^3\log T)\) 的时间内求出。

然后尝试着用 \(P\) 替代矩阵快速幂。我们考虑一下,我们需要求 \(B \circ A^x\) 的时候,快速幂是先计算 \(A^x\) 的,其中原理是 \(A^x = A^{e_1} \circ A^{e_2} \circ \cdots \circ A^{e_q}\),其中 \(e\) 是 \(x\) 二进制下的所有为 \(1\) 的位分别独立取出的值。而我们现在已经预处理好了这些 \(P\),如果我们直接用 \(B\) 每次对 \(P\) 做乘法,结果是一样的。

但这两者又有什么区别呢?当然有!我们发现 \(B\) 是一个 列向量,这个东西乘上 \(A\) 一次是 平方级别 的!

现在我们可以做到一次跳时间点 \(O((nw)^2\log(t_i - t_{i-1}))\),已经可以通过了。

总复杂度 \(O((nw)^3\log T + (nw)^2\sum\log(t_i - t_{i-1}))\)。

Code

/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : NOI2020 美食家
*/
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream> using namespace std;
typedef long long LL; const int N = 50 + 5;
const int W = 5;
const int logT = 31;
const int K = 200 + 5;
const LL inf = 5e16; int n, m, T, k;
int c[N];
struct activity {
int t, x, y;
} act[K << 1]; struct matrix {
LL e[N * W][N * W];
int R, C;
inline LL* operator [] (int p) { return e[p]; }
inline void set(LL v) {
for (int i = 1; i <= R; i++)
for (int j = 1; j <= C; j++)
e[i][j] = v;
}
} A, B, P[logT];
// P[k] : A^{2^k} inline matrix operator * (matrix a, matrix b) {
matrix c; c.R = a.R, c.C = b.C;
c.set(-inf);
for (int k = 1; k <= a.C; k++)
for (int i = 1; i <= a.R; i++)
for (int j = 1; j <= b.C; j++)
c[i][j] = max(c[i][j], a[i][k] + b[k][j]);
return c;
} void trans(matrix& B, int t) {
if (!t) return;
for (int i = 0; i < logT; i++)
if ((t >> i) & 1)
B = P[i] * B;
} signed main() {
freopen("delicacy3.in", "r", stdin);
// freopen("delicacy.out", "w", stdout); ios::sync_with_stdio(false);
cin >> n >> m >> T >> k; A.R = A.C = n * 5;
A.set(-inf); for (int i = 1; i <= n; i++)
for (int w = 1; w < W; w++)
A[i + n * w][i + n * (w - 1)] = 0;
for (int i = 1; i <= n; i++)
cin >> c[i];
for (int i = 1; i <= m; i++) {
int u, v, w; cin >> u >> v >> w;
A[v][u + n * (w - 1)] = c[u];
}
for (int i = 1; i <= k; i++)
cin >> act[i].t >> act[i].x >> act[i].y; P[0] = A;
for (int i = 1; i < logT; i++)
P[i] = P[i - 1] * P[i - 1];
B.R = n * 5, B.C = 1;
B.set(-inf);
B[1][1] = 0; sort(act + 1, act + 1 + k, [](activity& a, activity& b) {
return a.t < b.t;
});
int curT = 0;
for (int i = 1; i <= k; i++) {
int gap = act[i].t - curT;
trans(B, gap);
B[act[i].x][1] += act[i].y;
curT = act[i].t;
}
trans(B, T - curT); LL ans = B[1][1] + c[1];
cout << (ans < 0 ? -1 : ans) <<endl;
return 0;
}

【NOI2020】美食家(矩阵)的更多相关文章

  1. 洛谷 P6772 - [NOI2020]美食家(广义矩阵快速幂)

    题面传送门 题意: 有一张 \(n\) 个点 \(m\) 条边的有向图,第 \(0\) 天的时候你在 \(1\) 号城市,第 \(T\) 天的时候你要回到 \(1\) 号城市. 每条边上的边权表示从城 ...

  2. [NOI2020]美食家 题解

    题意分析 给出一个带权有向图,要求从节点 $1$ 出发,经过恰好 $T$ 的边权和,回到节点 $1$ ,求可经过的最大点权和.特别地,经过的边权和达到部分特殊数时,会有某个点的点权发生改变. 思路分析 ...

  3. P6772 [NOI2020]美食家

    题目大意 给你一个 \(n\) 个点,\(m\) 条边的有向图,每条边有一个权值 \(w_i\) ,每个节点有一个权值 \(a_i\) . 你从节点 \(1\) 出发,每经过一个节点就可以获得该点的权 ...

  4. [XIN算法应用]NOI2020美食家

    XIN(\(updated 2021.6.4\)) 对于很多很多的题目,发现自己并不会之后,往往会直接冲上一个XIN队算法,然而,这样 \(\huge{\text{鲁莽}}\) 的行为只能获得 TLE ...

  5. [NOI2020] 美食家

    很好,自己会做NOI签到题了,去年只要会这题,再多打点暴力,\(Ag\)到手,希望今年\(NOI\)同步赛过\(Ag\)线吧,得有点拿得出手的成绩证明啊. 考虑\(T\)非常大,\(n\)又很小. 想 ...

  6. XIN队算法

    XIN队算法 注:名称由莫队算法改编而来 从luogu搬过来了... \(newly\;upd:2021.7.8\) \(newly\;upd:2021.6.6\) OI至高算法,只要XIN队算法打满 ...

  7. 矩阵乘法优化DP复习

    前言 最近做毒瘤做多了--联赛难度的东西也该复习复习了. Warning:本文较长,难度分界线在"中场休息"部分,如果只想看普及难度的可以从第五部分直接到注意事项qwq 文中用(比 ...

  8. NOI2020网上同步赛 游记

    Day1 预计得分:\(32pts\)(我裂开了--) T1 美食家 表示考试的时候想到了关于矩阵快速幂的想法,甚至连分段后怎么处理都想好了,但是没有想到拆点,还有不知道怎么处理重边(这个考虑是多余的 ...

  9. NOI2020 同步赛划水记

    因为太菜了没去现场参加 NOI 就算去了估计也只能混个Fe(雾) "两天都会各有一道签到题,争取拿到70分.剩下的题每道题打30分暴力.每天130分,就能稳拿Ag了."--ls D ...

随机推荐

  1. 极客mysql16

    1.MySQL会为每个线程分配一个内存(sort_buffer)用于排序该内存大小为sort_buffer_size 1>如果排序的数据量小于sort_buffer_size,排序将会在内存中完 ...

  2. CSS 背景常用属性

    CSS 背景常用属性 background-color 这个属性过于简单, 以下写法均可 background-color:red; background-color:rgb(0,0,255); ba ...

  3. window.frames["id"].location使用

    由于最近需要维护一个老项目不得不去学习一些自己都没接触过的项目,老项目中虽然技术已经被淘汰,但是思想还是值得去学习探究的,无论是jsp,freemarker,freemarker这些模板引擎还是Vue ...

  4. [原题复现][2020i春秋抗疫赛] WEB blanklist(SQL堆叠注入、handler绕过)

    简介 今天参加i春秋新春抗疫赛 一道web没整出来 啊啊啊 好垃圾啊啊啊啊啊啊啊  晚上看群里赵师傅的buuoj平台太屌了分分钟上线 然后赵师傅还分享了思路用handler语句绕过select过滤.. ...

  5. C# 中的本地函数

    今天我们来聊一聊 C# 中的本地函数.本地函数是从 C# 7.0 开始引入,并在 C# 8.0 和 C# 9.0 中加以完善的. 引入本地函数的原因 我们来看一下微软 C# 语言首席设计师 Mads ...

  6. 新鲜出炉!阿里巴巴,京东,美团面经汇总,已拿offer!

    最近在后台收到了很多小伙伴的私信,说自己最近想好好准备一下,在不久后的秋招跳个槽往大厂冲击一下,想要我给大家整理出一份大厂面试题好用来做好准备.之前公司有点事比较忙就没回私信.最近好一点了我也是立马联 ...

  7. 解决adober reader已停止工作的问题

    公司电脑出现打开pdf阅读器后没到1分钟就闪退,应该是adobe做了什么正版审核策略,让安装了这个软件的电脑自动连接到adobe服务器验证导致. 解决方法:在C:\Windows\System32\d ...

  8. SAP PI接口ESR IA配置,几种常用的 XSL 转换文档模板

    在PI开发配置中字段映射一般分为Message Mapping(MM)和Imported Archives(IA)这两种形式.MM这种拉线的形式虽然看似方便,但是当接口更新和传输时往往比较麻烦,同时无 ...

  9. 攻克solo第五课(Mixolydian 音阶)

    相对于独奏来说,我们已经说过了很多关于solo或独奏的乐理和技巧.那么这篇文章,笔者将使用guitar pro7软件来跟大家分享Mixolydian 音阶的演奏技巧,以及如何在学习Mixolydian ...

  10. AFNetWorking 丢失数据

    问题描述: 使用AFNetWorking请求数据,请求成功,但是拿不到所需要的数据,但是使用其他平台都可以拿到数据. 原因分析: AFNetWorking无法解析. 解决方式: AFJSONRespo ...