zoukankan      html  css  js  c++  java
  • [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} imes 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}} imes G_1^{t_k-t_{k-1}} ]

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

    此时如果暴力搞到 (G_1^{t_k-t_{k-1}}) 就得到 (O((nw)^3klog_2T))优秀复杂度。不过 NOI Online 里面的技巧可以直接套过来:利用矩阵的结合律,我们直接用 (vec{F}) 去分别乘 (G_1) 的倍增幂。于是就优化到了 (O((nw)^2klog_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( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    模拟电梯运行
    用户需求调研报告
    NABC需求分析
    大道至简---读书随笔3
    二维环形数组求最大子数组和
    结对开发之求最大数组溢出问题
    结对开发之环形数组
    结对开发之电梯调度
    我看“微软拼音”
    团队开发项目之典型用户和用户场景
  • 原文地址:https://www.cnblogs.com/crashed/p/13540212.html
Copyright © 2011-2022 走看看