【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;
}

我还是太菜了啊

posted @ 2020-09-15 19:13  lahlah  阅读(20)  评论(0编辑  收藏  举报