【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;
}
相关推荐
欧特克_Glodon4 小时前
OpenCV计算机视觉开发入门与实践<二十七>:图像分割概述
c++·人工智能·opencv·计算机视觉
蒸蒸yyyyzwd4 小时前
cpp 选手秋招学习笔记 day21
c++·面试·八股
alphaTao4 小时前
LeetCode 每日一题 2026/8/24-2026/8/30
python·算法·leetcode
Interview Aid1124 小时前
TikTok OA 四题分享|半小时内 AC,题目基本都是实现题
java·开发语言·算法·面试·职场和发展
AI_yangxi6 小时前
短视频矩阵系统选哪家
大数据·人工智能·矩阵
顶点多余13 小时前
那些在算法中适合巩固的知识点---1
java·前端·算法
罗西的思考15 小时前
【Agentic RL / 强化学习框架】Molt 设计解读
人工智能·算法·机器学习
「QT(C++)开发工程师」15 小时前
C++ auto 用法详解
开发语言·c++
OPEN-F15 小时前
C++STL教程:容器适配器与实用工具
开发语言·c++
OPEN-F15 小时前
C++模板教程:变参模板、折叠表达式与SFINAE
java·开发语言·c++