【luogu P6772】 [NOI2020]美食家
好在没有买D类,不然就白给2万块了
同步赛的时候竟然连这道签到题都没做出来
后面发现是自己线代的知识还不够熟练,于是花两天重新学了一遍
还要加油啊
不扯了
题解
首先可以很容易想到矩阵乘法
但是边权不是1怎么搞?
看到w只有5,可以考虑拆点,把一个点拆成5个点,每个点向后一个点连边,边权为0,第一个点作为入点,最后一个点为出点。
这样做一次的时间复杂度是 O ( ( 5 n ) 3 l o g T ) O((5n)^3logT) O((5n)3logT)的,因为还有美食节,还要再乘以 k k k直接去世
考虑怎么优化,我们发现,矩阵乘法优化其实最后就是一个向量*一个矩阵,也就是说对于每个美食节,我们不需要把整个矩阵保留下来,只需要保留一个向量就行了,然后预处理矩阵的次幂就好了。每次只需要拿这个向量乘矩阵
向量乘矩阵是
O
(
(
5
n
)
2
)
O((5n)^2)
O((5n)2)
所以最后的时间复杂度就是
O
(
(
5
n
)
3
l
o
g
T
+
k
(
5
n
)
2
l
o
g
T
)
O((5n)^3logT+k(5n)^2logT)
O((5n)3logT+k(5n)2logT)
上面不理解可以康康代码:
#include<bits/stdc++.h>
#define ll long long
#define N 255
using namespace std;
const ll INF = (1ll << 61);
int tot;
struct MT {
ll a[N][N];
void clear() {
for(int i = 0; i < N; i ++)
for(int j = 0; j < N; j ++) a[i][j] = - INF;
}
} g[35];
ll A[N], B[N];
MT mul(MT a, MT b) {
MT c; c.clear();
for(int i = 1; i <= tot; i ++)
for(int j = 1; j <= tot; j ++)
for(int k = 1; k <= tot; k ++)
c.a[i][j] = max(a.a[i][k] + b.a[k][j], c.a[i][j]);
return c;
}
//------------上面是矩阵乘法
void Mul(MT a) {//向量乘矩阵,这里是行向量
for(int i = 1; i <= tot; i ++) B[i] = - INF;
for(int i = 1; i <= tot; i ++)
if(A[i] >= 0)
for(int j = 1; j <= tot; j ++)
B[j] = max(B[j], A[i] + a.a[i][j]);
for(int i = 1; i <= tot; i ++) A[i] = B[i];
}
struct F {
int t, x, y;
} fes[N];
int cmp(F x, F y) {
return x.t < y.t;
}
int n, m, T, k, c[N], id[N][7];
int main() {
scanf("%d%d%d%d", &n ,&m, &T, &k);
for(int i = 1; i <= n; i ++) scanf("%d", &c[i]);
for(int i = 1; i <= n; i ++) id[i][0] = ++ tot;
for(int i = 0; i <= 33; i ++) g[i].clear();
for(int i = 1; i <= m; i ++) {//拆点、编号 &连边
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
for(int j = 1; j < w; j ++) if(!id[u][j]) id[u][j] = ++ tot;
for(int j = 1; j < w; j ++) g[0].a[id[u][j - 1]][id[u][j]] = 0;
g[0].a[id[u][w - 1]][v] = c[v];
}
for(int i = 1; i <= k; i ++) scanf("%d%d%d", &fes[i].t, &fes[i].x, &fes[i].y);
sort(fes + 1, fes + 1 + k, cmp);//美食节
fes[0] = F{0, 0, 0}; fes[k + 1] = F{T, 0, 0};
for(int i = 1; i <= 30; i ++) g[i] = mul(g[i - 1], g[i - 1]);
A[1] = c[1]; for(int i = 2; i <= tot; i ++) A[i] = - INF;
for(int i = 1; i <= k + 1; i ++) {
int dt = fes[i].t - fes[i - 1].t;
for(int j = 30; ~j; j --) if((dt >> j) & 1) Mul(g[j]);//直接拿向量乘矩阵
A[fes[i].x] += fes[i].y;//把行向量对应位+美食节的收益
}
if(A[1] < 0) printf("-1");
else printf("%lld", A[1]);
return 0;
}
我还是太菜了啊