【LuoguP6772】美食家【DP】【矩阵】【倍增】

链接

题目大意:

有n个点,m条边,每个点有个权值c,每条边有长度w,要求刚好走过长度总共为T的路(可重复),从1号点回到1号点,最大化选择路径上每个点的权值的总和。

其中还有k个,在刚好走过t的长度时,在点u会有额外的权值v。

思路:

很容易想到状态为 f i u f_{i \ u} fi u的转移,表示走过i长度时,刚好在点u的最大权值,但时间复杂度显然不够。

假设k=0

考虑矩阵加速优化,原来的转移方程: f i v = m a x ( f i − w u + c v ) f_{i \ v} = max(f_{i - w \ u} + c_v) fi v=max(fi−w u+cv)

这里使用一个巧妙地思路,将每个点u拆成5个点(w的最大值), u i u_i ui向 u i + 1 u_{i+1} ui+1连一条边权为0的边,对于原图的每条边,从 u w u_w uw向 v 1 v_1 v1连一条边权为c的边,这样可以保证每次不能停在原地,且巧妙地将c融入矩阵之中,方便后续的乘法操作。(n *= 5)

然后根据转移方程,这里的矩阵加速就是 c i j = m a x ( a i k + b k j ) c_{i \ j} = max(a_{i \ k} + b_{k \ j}) ci j=max(ai k+bk j),用矩阵快快速幂实现。

k不等于0

相当于将从0到T的一整次矩阵快速幂分成k次来做,每次只需要在中断处将额外权值加入ans就可以。

这里还需要一个优化。先将矩阵指数幂预处理出来。每次从last->now的长度时,将now-last进行指数分解( 2 a + 2 b + ... ... ) 2^a+2^b+......) 2a+2b+......),每次只需要从预处理出的指数幂中拿出矩阵进行ans更新,不需要log(now - last)在算一次。

时间复杂度: O ( l o g T ( 5 n ) 3 + k l o g T ( 5 n ) 2 ) O(log \ T \ (5n)^3 + k \ log \ T \ (5n)^2) O(log T (5n)3+k log T (5n)2)

code

cpp 复制代码
#include<iostream>
#include<cstdio>
#include<algorithm>

#define ll long long

using namespace std;

const ll MAXN = 505, inf = 1e16;

ll n, m, T, k;
ll C[MAXN], a[MAXN][MAXN], ans[MAXN], b[MAXN];
ll Pow[31][MAXN][MAXN];

struct Reward {
    ll t, u, v;
} t[MAXN];

bool cmp(Reward x, Reward y) {
    return x.t < y.t;
}

void mul_(ll to, ll fro) {
    for(ll i = 1; i <= n; i ++)
        for(ll j = 1; j <= n; j ++) {
            Pow[to][i][j] = -inf;
            for(ll k = 1; k <= n; k ++)
                Pow[to][i][j] = max(Pow[to][i][j], Pow[fro][i][k] + Pow[fro][k][j]);
        }
}

void mul_ans(ll cnt) {
    for(ll i = 1; i <= n; i ++) b[i] = ans[i], ans[i] = -inf;
    for(ll j = 1; j <= n; j ++)
        for(ll k = 1; k <= n; k ++)
            ans[j] = max(ans[j], b[k] + Pow[cnt][k][j]);
}

void init_() {
    for(ll i = 1; i <= n; i ++)
        for(ll j = 1; j <= n; j ++) Pow[0][i][j] = a[i][j];
    for(ll i = 1; i <= 30; i ++) mul_(i, i - 1);
}

void Update(ll B) {
    ll j = 30;
    while(B) {
        if(B >= (1 << j)) mul_ans(j), B -= 1 << j;
        j --;
    }
}

int main() {
    scanf("%lld%lld%lld%lld", &n, &m, &T, &k);
    for(ll i = 1; i <= n; i ++) scanf("%lld", &C[i]);
    n *= 5;
    for(ll i = 1; i <= n; i ++)
        for(ll j = 1; j <= n; j ++) a[i][j] = ans[i] = -inf;
    for(ll i = 1; i <= n / 5; i ++)
        for(ll j = 1; j < 5; j ++) a[(i - 1) * 5 + j][(i - 1) * 5 + j + 1] = 0;
    for(ll i = 1; i <= m; i ++) {
        ll u, v, w;
        scanf("%lld%lld%lld", &u, &v, &w);
        a[(u - 1) * 5 + w][(v - 1) * 5 + 1] = C[v];
    }
    init_();
    for(ll i = 1; i <= k; i ++) scanf("%lld%lld%lld", &t[i].t, &t[i].u, &t[i].v);
    sort(t + 1, t + 1 + k, cmp);
    ans[1] = C[1];
    for(ll i = 1; i <= k; i ++) {
        ll B = t[i].t - t[i - 1].t;
        Update(B);
        ans[(t[i].u - 1) * 5 + 1] += t[i].v;
    }
    if(t[k].t != T) Update(T - t[k].t);
    if(ans[1] < 0) printf("-1");
    else printf("%lld", ans[1]);
    return 0;
}
相关推荐
地平线开发者14 分钟前
模型部署|如何解决算子约束
深度学习·算法·自动驾驶
骑着蜗牛撵大象32727 分钟前
告别内存泄漏:LLMManager架构设计与智能指针在AI SDK中的智慧选型
c++·ai·架构
Navigator_Z1 小时前
LeetCode //C - 1254. Number of Closed Islands
c语言·算法·leetcode
deepseek231 小时前
GPT-6 Astra 破解 FrontierMath 九年悬案拆解:调和熵投票规则反证核恒非空,局部搜索如何终结反例悬赏
人工智能·算法·ai agent
汉克老师1 小时前
CSP-J 2026 初赛试题解析(第二部分:阅读程序题(第一题))精讲
c++·csp-j·小学生·学c++编程
syagain_zsx1 小时前
算法基础篇 · 04 前缀和(C++ 题解)
c++·算法·前缀和
汉克老师2 小时前
CSP-J 2026 初赛试题解析(第三部分:完善程序题(第一题))精讲
c++·csp-j·小学生·学c++编程
青山是哪个青山2 小时前
LeetCode 123:买卖股票的最佳时机 III
算法
haluhalu.3 小时前
初识 Protobuf:微服务跨语言通信的一份契约
java·c语言·开发语言·c++·python
Zane19943 小时前
写对二分查找有多难?Java集合框架的作者也曾栽在一行mid计算上
算法