ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「NOI2020」 美食家 【矩阵快速幂】

2021-07-01 23:31:23  阅读:248  来源: 互联网

标签:le 美食家 矩阵 NOI2020 5n mathcal 复杂度 dp


loj3339 美食家

Description

一张 \(n\) 个点 \(m\) 条边的有向图,每条边有权值 \(w_i\) 表示走完该边需要的时间,每次走到点 \(i\) 都可以获得 \(c_i\) 的收益。

在 \(0s\) 时,你从起点 \(1\) 出发,不断沿图中的边走知道 \(Ts\) 时回到 \(1\) 号点,中途不能在任何点上停留 。同时还有 \(k\) 个特殊收益,第 \(i\) 个表示如果你在 \(t_i\) 天时恰好在城市 \(x_i\) ,则会额外获得 \(v_i\) 的收益。请求出最大的总收益。

\(n\le 50,m\le 501,T\le 10^9,k\le 200,1\le w_i\le 5,1\le c_i\le 52501\)。

Solution

首先容易想到朴素 \(DP\),设 \(dp_{t,x}\) 表示 \(ts\) 时恰好到达点 \(x\) 的所有方案中的最大总收益,然后暴力枚举下一步走的边进行转移:

\[dp_{t,v}=\max(dp_{t,v},dp_{t-1,u}+c_v+w_{t,v}) \]

其中 \(w_{t,v}\) 为 \(t\) 时刻 \(v\) 点的特殊收益。

这样做的复杂度时 \(\mathcal O(mT)\) 的。

\(T\) 的数据范围提示我们使用矩阵快速幂,注意到题目中保证 \(w_i\le 5\),因此 \(dp_{t,x}\) 只可能有 \(\ge t-5\) 时间的 \(dp\) 值转移到时间 \(t\) 的所有 \(dp\) 值,因此会影响时间 \(t\) 的 \(dp\) 值的点只有 \(5n\) 个,于是可以直接将这 \(5n\) 个点的转移写成矩阵。

具体地,设当前需要求 \(t\) 时刻的所有 \(dp\) 值,影响它们的所有 \(5n\) 个 \(dp\) 值可以写成一个列向量:

\[\begin{pmatrix} dp_{1,t-5}\\ dp_{2,t-5}\\ \dots\\ dp_{n,t-5}\\ dp_{1,t-4}\\ \dots\\ dp_{n,t-4}\\ \dots\\ dp_{1,t-1}\\ \dots\\ dp_{n,t-1} \end{pmatrix} \]

那么新的列向量需要继承 \(t-4\sim t-1\) 的 \(dp\) 值转移得到 \(t\) 的 \(dp\) 值。这个相信大家都会,本题中 \(dp\) 的转移是关于 \(+\) 与 \(\max\) 的,但我们也可以跟普通矩阵快速幂一样做,只需要将一般矩阵乘法 \((+,\times)\),变为 \((\max,+)\)

大力写出转移矩阵,由于在 \(k\) 个有特殊收益的时间内需要特殊处理,因此可以将时间 \(T\) 分为 \(k\) 段依次处理,对于每一段分别进行矩阵快速幂后用列向量左乘,然后再单独处理特殊点的转移,这样做的复杂度就是 \(\mathcal O((5n)^3k\log T)\) 了。

考虑优化,每次暴力进行矩阵快速幂实际上没有必要,因为每次都对同一个矩阵取幂,那么可以预处理该矩阵的 \(2^k\) 次幂,复杂度为 \(\mathcal O((5n)^3\log T)\),对于每一段时间,设需要乘上一个矩阵的 \(c\) 次方,那么将 \(c\) 拆为多个 \(2^k\) 相加,用答案的列向量分别去乘这些矩阵,单次乘法的复杂度是 \(\mathcal O((5n)^2)\),因此总复杂度为 \(\mathcal O((5n)^3\log T+(5n)^2k\log T)\),可以通过此题。

本题的优化方法算是矩阵快速幂的一种常见套路了,建议记录一下。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=251;
const ll inf=-1e15;
int d;
struct matrix{
	ll c[N][N];
	inline void init(ll x){
		for(int i=0;i<d;++i)
			for(int j=0;j<d;++j) c[i][j]=i==j?x:inf;
	}
}pw[30];
matrix operator *(const matrix &A,matrix &B){
	matrix ret;
	for(int i=0;i<d;++i)
		for(int j=0;j<d;++j){
			ret.c[i][j]=inf;
			for(int k=0;k<d;++k) ret.c[i][j]=max(ret.c[i][j],A.c[i][k]+B.c[k][j]);
		}
	return ret;
}
int n,m,T,k,c[N];
inline int id(int x,int y){return y*n+x-1;}
struct query{
	int t,x,y;
}q[N];
bool operator <(const query &a,const query &b){return a.t<b.t;}
typedef vector<ll> vec;//列向量
vec operator *(const vec &A,const matrix &B){
	vec ret;ret.resize(d);
	for(int i=0;i<d;++i){
		ret[i]=inf;
		for(int j=0;j<d;++j)
			ret[i]=max(ret[i],A[j]+B.c[i][j]);
	}
	return ret;
} 
int main(){
//	freopen("delicacy.in","r",stdin);
//	freopen("delicacy.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&T,&k);d=n*5;
	pw[0].init(inf);
	for(int i=1;i<=n;++i){
		scanf("%d",&c[i]);
		for(int j=0;j<4;++j) pw[0].c[id(i,j)][id(i,j+1)]=0;
	}
	for(int i=1,u,v,w;i<=m;++i){
		scanf("%d%d%d",&u,&v,&w);
		pw[0].c[id(v,4)][id(u,5-w)]=c[v];	
	}
	for(int i=1;(1<<i)<=T;++i) pw[i]=pw[i-1]*pw[i-1];
	for(int i=1;i<=k;++i) scanf("%d%d%d",&q[i].t,&q[i].x,&q[i].y);
	q[++k]=(query){T,1,0};
	sort(q+1,q+k+1);
	vec ret;ret.resize(d);
	for(int i=0;i<d;++i) ret[i]=inf;
	ret[id(1,4)]=c[1];
	for(int l=1,r=0;l<=k;l=r+1){
		r=l;
		while(r<k&&q[r+1].t==q[l].t) ++r;
		int tmp=q[l].t-q[l-1].t;
//		cout<<l<<" "<<r<<" "<<tmp<<endl;
		if(tmp>0){
			for(int i=0;(1<<i)<=tmp;++i)
				if(tmp&(1<<i)) ret=ret*pw[i];
		}
		for(int i=l;i<=r;++i)
			ret[id(q[i].x,4)]+=q[i].y;
	}
	printf("%lld\n",ret[id(1,4)]<=-1?-1:ret[id(1,4)]);
	return 0;
}

标签:le,美食家,矩阵,NOI2020,5n,mathcal,复杂度,dp
来源: https://www.cnblogs.com/tqxboomzero/p/14961395.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有