P6772 [NOI2020] 美食家 题解
一道不错的题目。
一个朴素的 dp 就是 表示时间 时从 1 走到 的最大美味值,就有转移 ,结合性质 1 判下环有 50 分。
考虑 并且 的情况,就是一个经典问题: 点 边,问恰好经过 条边情况下的最长路,有 。
考虑点权转边权,每一条边边权是指向点的点权,用邻接矩阵存一下就有 。
这实际上是一种矩阵形式,新定义矩阵乘法 有 ,然后可证这个运算满足结合律,因此可以考虑将 看成一个行向量(一行的矩阵),就有 ,其中 其余为 -INF,邻接矩阵 中没有边的都是 -INF,最后答案 ,如果是负的就说明无解。
然后考虑 的情况,考虑一条边 ,拆成若干个点 ,这样就消除了这个情况,但是按边来拆点点数是 , 比 大得多,不大行,因此考虑按点来拆,将一个点拆成 5 个点,,边权全部都是 0,一条边 就连边 ,意义在于前 条边边权都是 0 没有贡献,只有最后一条边有贡献,这样可以过 的点。
考虑 的情况,由于 因此考虑按照 个美食节(按时间升序排序)划分,对于每两个美食节之间我们使用朴素的 dp 转移,然后每转移到一个美食节 那么 ,这样可以做到复杂度 ,能获得 85 分, 是每次朴素 dp 转移时指数复杂度。
注意到我们可以预处理出 ,就是二进制处理,预处理的复杂度是 ,这样每次朴素 dp 时我们可以直接二进制拆出来而不需要每次重复计算 ,又因为 实际上是一个行向量(只有一行),所以 乘上这些矩阵的单次实际复杂度是 ,成功优化了矩阵快速幂的 复杂度,两个复杂度加起来就是总复杂度可以过。
Code:
/*
========= Plozia =========
Author:Plozia
Problem:P6772 [NOI2020] 美食家
Date:2022/10/9
========= Plozia =========
*/
#include <bits/stdc++.h>
typedef long long LL;
LL Max(LL fir, LL sec) { return (fir > sec) ? fir : sec; }
const int MAXN = 50 + 5, MAXM = 501 + 5, MAXK = 200 + 5;
int n, m, t, k, ys[MAXN][7];
LL c[MAXN];
struct node
{
int t, x; LL y;
bool operator <(const node &fir)const { return t < fir.t; }
}a[MAXK];
struct Matrix
{
int n, m; LL a[MAXN * 5][MAXN * 5];
Matrix operator *(const Matrix &fir)
{
Matrix tmp; tmp.n = n, tmp.m = fir.m; memset(tmp.a, -0x3f, sizeof(tmp.a));
for (int i = 1; i <= n; ++i)
for (int k = 1; k <= m; ++k)
for (int j = 1; j <= fir.m; ++j)
if (a[i][k] >= 0 && fir.a[k][j] >= 0) tmp.a[i][j] = Max(tmp.a[i][j], a[i][k] + fir.a[k][j]);
return tmp;
}
}Base, dis, fact[30];
int Read()
{
int sum = 0, fh = 1; char ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
return sum * fh;
}
int main()
{
n = Read(), m = Read(), t = Read(), k = Read(); dis.n = 5 * n, dis.m = 5 * n; memset(dis.a, -0x3f, sizeof(dis.a));
for (int i = 1; i <= n; ++i) c[i] = Read();
for (int i = 1; i <= n; ++i) for (int j = 1; j <= 5; ++j) ys[i][j] = 5 * (i - 1) + j;
for (int i = 1; i <= n; ++i) for (int j = 1; j < 5; ++j) dis.a[ys[i][j]][ys[i][j + 1]] = 0;
for (int i = 1; i <= m; ++i)
{
int x = Read(), y = Read(), z = Read();
dis.a[ys[x][z]][ys[y][1]] = c[y];
}
fact[0] = dis; for (int i = 1; i <= 29; ++i) fact[i] = fact[i - 1] * fact[i - 1];
for (int i = 1; i <= k; ++i) a[i].t = Read(), a[i].x = Read(), a[i].y = Read();
std::sort(a + 1, a + k + 1); Base.n = 1, Base.m = 5 * n; memset(Base.a, -0x3f, sizeof(Base.a)); Base.a[1][1] = c[1];
int Last = 0; a[k + 1] = (node){t, 0, 0};
for (int i = 1; i <= k + 1; ++i)
{
int tmp = a[i].t - Last;
for (int j = 0; j <= 29; ++j) if ((tmp >> j) & 1) Base = Base * fact[j];
if (Base.a[1][ys[a[i].x][1]] >= 0) Base.a[1][ys[a[i].x][1]] += a[i].y; Last = a[i].t;
}
if (Base.a[1][1] < 0) puts("-1"); else printf("%lld\n", Base.a[1][1]); return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具