链接
题目大意:
有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;
}