P6772 [NOI2020]美食家

H y p e r l i n k Hyperlink Hyperlink

https://www.luogu.com.cn/problem/P6772


D e s c r i p t i o n Description Description

给定一张 n n n个点 m m m条有向边,第 i i i个点的权值是 c i c_i ci,给定一个时间 T T T,经过第 j j j条边需要 t j t_j tj的时间,不允许在某一个点停留,求运行 T T T秒到达1经过的点的最大权值和(每个点被到达一次可以获得一次贡献),有 k k k个时刻某些点会有额外贡献

n ≤ 50 , m ≤ 500 , T ≤ 1 0 6 , t j ≤ 5 , k ≤ 200 n\leq 50,m\leq 500,T\leq 10^6,t_j\leq 5,k\leq 200 n50,m500,T106,tj5,k200


S o l u t i o n Solution Solution

我们观察到 t j t_j tj很小,最大是5,所以我们可以把每个点拆成五个,四个表示在路上(以及还差几秒),最后一个表示正好在这一个点上,那么我们令这五个点相邻点的权值为0,然后路径长度正好对应连到哪个点上,权值即为 c 去 到 的 点 c_{去到的点} c

那么我们就构造好了矩阵 A [ i ] [ j ] A[i][j] A[i][j],表示经过1秒, i i i走到 j j j的最大权值和
容易证明其是满足结合律的,注意直接乘可能会 T T T,我们最后只关心能否到达1,所以我们直接把 A A A的第一行存到 a a a中,改成向量乘矩阵

此时如果没有那 k k k个点,我们即可 O ( ( 5 n ) 2 l o g k ) O((5n)^2log k) O((5n)2logk)解决该题

对于中间的 k k k个点,我们用类似快速乘的方法,将每次的间隔划分成 2 2 2次方求和的形式,然后对应乘(具体做法是设 f k f_k fk表示 A 2 k A^{2^k} A2k

这样问题就得到解决,时间复杂度: O ( ( 5 n 2 ) l o g k + 30 n 3 ) O((5n^2)log k+30n^3) O((5n2)logk+30n3)


C o d e Code Code

#include 
#include
#include
#include
#define LL long long
using namespace std;int n,m,k,x,y;
LL A[251][251],f[31][251][251],a[251],T,c[51],z,now;
struct node{LL T,y;int x;}Q[211];
inline bool cmp(node x,node y){return x.T<y.T;}
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline void mulself()
{
	LL C[251][251];
	memset(C,0xcf,sizeof(C));
	for(register int i=1;i<=5*n;i++)
	 for(register int j=1;j<=5*n;j++)
	  for(register int k=1;k<=5*n;k++)
	    C[i][j]=max(C[i][j],A[i][k]+A[k][j]);
	memcpy(A,C,sizeof(C));
	return;
}
inline void mul(int q)
{
	LL cs[251];
	memset(cs,0xcf,sizeof(cs));
	for(register int i=1;i<=5*n;i++)
	 for(register int j=1;j<=5*n;j++)
	cs[j]=max(cs[j],a[i]+f[q][i][j]);
	memcpy(a,cs,sizeof(a));
	return;
}
signed main()
{
	memset(a,0xcf,sizeof(a));
	memset(f,0xcf,sizeof(f));
	memset(A,0xcf,sizeof(A));
	n=read();m=read();T=read();k=read();
	for(register int i=1;i<=n;i++) c[i]=read();
	for(register int i=1;i<=n;i++)
	 for(register int j=1;j<=4;j++)
	  A[5*i-j][5*i-j+1]=0;
	for(register int i=1;i<=m;i++)
	{
		x=read();y=read();z=read();
		A[5*x][5*y-z+1]=c[y];
	}
	memcpy(f[0],A,sizeof(f[0]));
	a[5]=c[1];
	for(register int i=1;i<=30;i++)
	{
		mulself();
		memcpy(f[i],A,sizeof(f[i]));
	}
	for(register int i=1;i<=k;i++) Q[i].T=read(),Q[i].x=read(),Q[i].y=read();
	Q[++k]=(node){T,0,0};
	sort(Q+1,Q+1+k,cmp);now=0;
	for(register int i=1;i<=k;i++)
	{
		LL jg=Q[i].T-now;
		for(register int j=0;j<=30;j++) if((jg>>j)&1) mul(j);
		a[5*Q[i].x]+=Q[i].y;
		now=Q[i].T;
	}
	if(a[5]<0) puts("-1");else
	printf("%lld",a[5]);
}

你可能感兴趣的:(矩阵乘法,P6772,NOI2020,美食家)