1. 程式人生 > 實用技巧 >【洛谷P6772】美食家

【洛谷P6772】美食家

題目

題目連結:https://www.luogu.com.cn/problem/P6772
坐落在 Bzeroth 大陸上的精靈王國擊退地災軍團的入侵後,經過十餘年的休養生息,重新成為了一片欣欣向榮的樂土,吸引著八方遊客。小 W 是一位遊歷過世界各地的著名美食家,現在也慕名來到了精靈王國。

精靈王國共有 \(n\) 座城市,城市從 \(1\)\(n\) 編號,其中城市 \(i\) 的美食能為小 W 提供 \(c_i\) 的愉悅值。精靈王國的城市通過 \(m\)單向道路連線,道路從 \(1\)\(m\) 編號,其中道路 \(i\) 的起點為城市 \(u_i\) ,終點為城市 \(vi_\)

,沿它通行需要花費 \(w_i\) 天。也就是說,若小 W 在第 \(d\) 天從城市 \(u_i\) 沿道路 \(i\) 通行,那麼他會在第 \(d + w_i\) 天到達城市 \(v_i\)

小 W 計劃在精靈王國進行一場為期 \(T\) 天的旅行,更具體地:他會在第 \(0\) 天從城市 \(1\) 出發,經過 \(T\) 天的旅行,最終在恰好第 \(T\)回到城市 \(1\) 結束旅行。由於小 W 是一位美食家,每當他到達一座城市時(包括第 \(0\) 天和第 \(T\) 天的城市 \(1\)),他都會品嚐該城市的美食並獲得其所提供的愉悅值,若小 W 多次到達同一座城市,他將獲得多次愉悅值

。注意旅行途中小 W 不能在任何城市停留,即當他到達一座城市且還未結束旅行時,他當天必須立即從該城市出發前往其他城市。

對於上圖,小 W 一種為期 \(11\) 天的可行旅遊方案為 \(1 \to 2 \to 1 \to 2 \to 3 \to 1\)

  • \(0\) 天,小 W 從城市 \(1\) 開始旅行,獲得愉悅值 \(1\) 並向城市 \(2\) 出發。
  • \(1\) 天,小 W 到達城市 \(2\),獲得愉悅值 \(3\) 並向城市 \(1\) 出發。
  • \(4\) 天,小 W 到達城市 \(1\),獲得愉悅值 \(1\) 並向城市 \(2\) 出發。
  • \(5\) 天,小 W 到達城市 \(2\)
    ,獲得愉悅值 \(3\) 並向城市 \(3\) 出發。
  • \(7\) 天,小 W 到達城市 \(3\),獲得愉悅值 \(4\) 並向城市 \(1\) 出發。
  • \(11\) 天,小 W 到達城市 \(1\),獲得愉悅值 \(1\) 並結束旅行。
  • 小 W 在該旅行中獲得的愉悅值之和為 \(13\)

此外,精靈王國會在不同的時間舉辦 \(k\) 次美食節。具體來說,第 \(i\) 次美食節將於第 \(t_i\) 天在城市 \(x_i\) 舉辦,若小 W 第 \(t_i\) 天時恰好在城市 \(x_i\),那麼他在品嚐城市 \(x_i\) 的美食時會額外得到 \(y_i\) 的愉悅值。現在小 W 想請作為精靈王國接待使者的你幫他算出,他在旅行中能獲得的愉悅值之和的最大值

思路

首先如果沒有美食節和路徑長度,那麼這道題就是裸的矩陣乘法。
我們發現這道題的邊權不超過 \(5\),所以考慮將每條邊拆成 \(5\) 條邊來跑,這樣還是相當於邊權為 \(1\),但是點的數量為 \(5n\leq 250\)
此時如果沒有美食節,那麼仍然直接矩陣乘法即可。
加入了美食節後,顯然要在每兩個美食節中間用矩陣快速冪計算出這幾天的答案,然後加上美食節貢獻。
但是直接這樣做是 \(O(kn^3\log t)\) 的,不可接受。
發現每次都要計算兩次美食節之間的代價大量運算重複,所以可以先預處理出矩陣的 \(2^i\) 冪,然後每次 \(O(\log t)\) 倍增求出。
然後答案是一個 \(1\times n\) 的矩陣,向量乘矩陣的複雜度是 \(O(n^2)\) 的,這樣時間複雜度就被我們降到了 \(O(kn^2\log t)\)
總時間複雜度 \(O(n^3\log t+kn^2\log t)\)。此處 \(n\) 等於原題中 \(5n\)

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=260,LG=30;
int n,m,t,k,tot,maxn,a[N];

int ID(int x,int y)
{
	return y*n+x;
}

struct node
{
	int t,x,a;
}b[N];

bool cmp(node x,node y)
{
	return x.t<y.t;
}

struct Matrix
{
	ll a[N][N];
	Matrix() { memset(a,0xcf,sizeof(a)); }
	
	friend Matrix operator *(const Matrix &a,const Matrix &b)
	{
		Matrix c;
		for (int i=1;i<=maxn;i++)
			for (int j=1;j<=tot;j++)
				for (int k=1;k<=tot;k++)
					c.a[i][j]=max(c.a[i][j],a.a[i][k]+b.a[k][j]);
		return c;
	}
}ans,f[LG+1];

Matrix fpow(Matrix a,int k)
{
	for (int i=LG;i>=0;i--)
		if (k&(1<<i)) a=a*f[i];
	return a;
}

int main()
{
	scanf("%d%d%d%d",&n,&m,&t,&k);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		for (int j=0;j<4;j++)
			f[0].a[ID(i,j)][ID(i,j+1)]=0;
	}
	tot=5*n;
	for (int i=1,x,y,z;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		f[0].a[ID(x,z-1)][y]=a[y];
	}
	maxn=tot;
	for (int i=1;i<=LG;i++)
		f[i]=f[i-1]*f[i-1];
	maxn=1;
	for (int i=1;i<=k;i++)
		scanf("%d%d%d",&b[i].t,&b[i].x,&b[i].a);
	sort(b+1,b+1+k,cmp);
	b[k+1].t=t;
	ans.a[1][1]=a[1];
	ans=fpow(ans,b[1].t);
	for (int i=1;i<=k;i++)
	{
		ans.a[1][b[i].x]+=b[i].a;
		ans=fpow(ans,b[i+1].t-b[i].t);
	}
	ll WYCtxdy=ans.a[1][1];
	if (WYCtxdy<0) printf("-1");
		else printf("%lld",WYCtxdy);
	return 0;
}