[NOI2020]美食家

题目

点这里看题目。

分析

NOI 里面也有我会做的题目?

显然不能把 \(T\) 放到状态里面,于是考虑用活动作为状态。

\(f(u,i)\):第 \(i\) 个活动开始的时候,位于 \(u\) 城市的最大愉悦值。

转移如下:

\[f(u,i)=\max_v\{f(v,i-1)+g(v,u,t_i-t_{i-1})\}+y_i[u=x_i] \]

其中 \(g(v,u,k)\) 表示用 \(k\) 的时间从 \(v\) 走到 \(u\) 的最大愉悦值。

然后继续发现 \(g\) 的转移:

\[g(u,v,k)=\max_{w}\{g(u,w,k-1)+g(w,v,1)\} \]

然后发现这个转移非常的矩阵乘法,于是考虑将 \(g(...,...,t)\) 用矩阵表述出来,设之为 \(G_t\)

但是这里有一个小小的问题:常规的矩阵乘法,处理的图都是不带边权的。这道题中该怎么处理边权呢?

答案是:拆点。对于一个点,我们将它拆成 5 个点,表示在这个点上走了 0 步、1 步、 2 步 ...... 的状态。这样一条权为 \(w\) 的边就可以被拆成 \(w\) 条权为 1 的边,我们就可以在这个图上做矩阵乘法了。

我觉得学过关于图的矩阵乘的朋友都应该知道这个技巧吧。这里就咕掉细节了。

好,拆完点之后我们就真的可以用 \(G_t\) 来描述 \(g(...,...,t)\) 了。

继续观察转移发现有:

\[G_t=G_{t-1}\times G_1 \]

于是就有:

\[G_t=G_1^t \]

于是 \(G_t\) 就是 \(G_1\) 的幂,于是就可以想到预处理 \(G_1\)\(2^k\) 次幂。于是我们就可以快速地得到 \(G_1\) 的幂。

回来看 \(f\) 的转移,我们发现 ...... 它还是很像一个向量乘以一个矩阵。

于是我们就可以直接用 \(\vec{F_k}\) 表示 \(f(...,k)\) ,然后转移就变成了:

\[\vec{F_k}=\vec{F_{k-1}}\times G_1^{t_k-t_{k-1}} \]

对于 \(u=x_i\) 的情况,我们需要在乘法之后单独处理一下。

此时如果暴力搞到 \(G_1^{t_k-t_{k-1}}\) 就得到 \(O((nw)^3k\log_2T)\)优秀复杂度。不过 NOI Online 里面的技巧可以直接套过来:利用矩阵的结合律,我们直接用 \(\vec{F}\) 去分别乘 \(G_1\) 的倍增幂。于是就优化到了 \(O((nw)^2k\log_2T)\) ,足以通过本题。

本题的一些有价值的点:

  1. 带边权的图结合矩阵的拆点技巧。
  2. 向量乘矩阵幂的优化技巧。

代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>

typedef long long LL;

const LL INF = -0xc0c0c0c0c0c0c0c0;

const int MAXS = 255;
const int MAXM = 510, MAXLOG = 35;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MAX( const _T a, const _T b )
{
	return a > b ? a : b;
}

struct matrix
{
	LL mat[MAXS][MAXS];
	int n, m;
	
	matrix() { n = m = 0, memset( mat, 0xc0, sizeof mat ); }
	matrix( const int N ) { n = m = N, memset( mat, 0xc0, sizeof mat ); }
	matrix( const int N, const int M ) { n = N, m = M, memset( mat, 0xc0, sizeof mat ); }
	
	LL* operator [] ( const int indx ) { return mat[indx]; }
	
	matrix operator * ( matrix b ) const
	{
		matrix ret( n, b.m );
		for( int i = 1 ; i <= n ; i ++ )
			for( int k = 1 ; k <= m ; k ++ )
				if( mat[i][k] > -INF )
					for( int j = 1 ; j <= ret.m ; j ++ )
						ret[i][j] = MAX( ret[i][j], mat[i][k] + b[k][j] );
		return ret;
	}
	
	void operator *= ( matrix b ) { *this = *this * b; }
};

struct festival
{
	int t, x, y;
	festival() { t = x = y = 0; }
	festival( const int T, const int X, const int Y ) { t = T, x = X, y = Y; }
	bool operator < ( const festival &b ) const { return t < b.t; }
}F[MAXM];

matrix G[MAXLOG];
matrix dp;

int fr[MAXM], to[MAXM], W[MAXM];
int C[MAXM], mx[MAXM] = {}, id[MAXM];
int N, M, K, T, tot = 1, lg2;

void upt( LL &x, const LL v ) { x = MAX( x, v ); }
void addEdge( const int u, const int v, const LL w ) { upt( G[0][u][v], w ); }

void init()
{
	std :: sort( F + 1, F + 1 + K );
	
	for( int i = 1 ; i <= N ; i ++ ) id[i] = tot, tot += mx[i]; 
	-- tot, G[0] = matrix( tot, tot );
	for( int i = 1 ; i <= N ; i ++ ) 
		for( int j = 0 ; j < mx[i] - 1 ; j ++ )
			addEdge( id[i] + j, id[i] + j + 1, 0 );
	for( int i = 1 ; i <= M ; i ++ ) addEdge( id[fr[i]] + W[i] - 1, id[to[i]], C[to[i]] );
	
	lg2 = log2( T );
	for( int i = 1 ; i <= lg2 ; i ++ ) G[i] = G[i - 1] * G[i - 1];
}

int main()
{
	read( N ), read( M ), read( T ), read( K );
	for( int i = 1 ; i <= N ; i ++ ) read( C[i] );
	for( int i = 1 ; i <= M ; i ++ ) read( fr[i] ), read( to[i] ), read( W[i] ), mx[fr[i]] = MAX( mx[fr[i]], W[i] );
	for( int i = 1 ; i <= K ; i ++ ) read( F[i].t ), read( F[i].x ), read( F[i].y );
	F[++ K] = festival( T, 1, 0 );
	init();
	
	dp = matrix( 1, tot ), dp[1][id[1]] = C[1];
	for( int i = 1 ; i <= K ; i ++ )
	{
		int stp = F[i].t - F[i - 1].t;
		for( int k = lg2 ; ~ k ; k -- )
			if( stp & ( 1ll << k ) )
				dp *= G[k];
		dp[1][id[F[i].x]] += F[i].y;
	}
	LL ans = dp[1][id[1]];
	if( ans < 0 ) puts( "-1" );
	else write( ans ), putchar( '\n' );
	return 0;
}
posted @ 2020-08-21 11:44  crashed  阅读(220)  评论(0编辑  收藏  举报