https://www.luogu.com.cn/problem/P6772
给定一张 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 n≤50,m≤500,T≤106,tj≤5,k≤200
我们观察到 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)
#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]);
}