1. 程式人生 > 實用技巧 >C#中的release和debug模式

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)\)

用矩陣表述出來,設之為 \(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;
}