C#中的release和debug模式
題目
點這裡看題目。
分析
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)\)
但是這裡有一個小小的問題:常規的矩陣乘法,處理的圖都是不帶邊權的。這道題中該怎麼處理邊權呢?
答案是:拆點。對於一個點,我們將它拆成 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}}\)
本題的一些有價值的點:
- 帶邊權的圖結合矩陣的拆點技巧。
- 向量乘矩陣冪的優化技巧。
程式碼
#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;
}