[NOI2020]美食家
本题要用矩阵快速幂。
这类问题是这样的:边有边权,求从 i 出发经过恰好 k 条边走到 j 的最长路。
对这种题可以想到 DP。设 dpk,i,j 表示从 i 出发经过恰好 k 条边走到 j 的最长路,G 为邻接矩阵,则有转移
dpk,i,j=maxp{dpk−1,i,p+Gp,j}
定义一个广义矩阵乘法
Ci,j=maxk{Ai,k+Bk,j}
把 dpk 看成矩阵,则上式可以改写为
dpk=dpk−1×G
因为这个广义矩阵乘法满足结合律,所以有
dpk=dp0×G^k
这样子就可以做到 O(n^3logk)。
首先,我们把每个点的点权作为它所有入边的边权,再特判起始点的点权(即把 dp0,1,1 设成 c1)即可。
其次,因为 wi 很小,所以考虑把边 e 拆成若干个点 ei 表示在这条边上的第 i 天。这样子对于原图中一条边 e=(u,v,w),我们可以拆成 (u,e1,0),(e1,e2,0),⋯,(ew−2,ew−1,0),(ew−1,v,cv)(w=1 时是 (u,v,cv))。
最后,本题中还有 k 个美食节。我们只需要在时间相邻的两个美食节间乘 G 转移,然后在美食节那一天乘一个举办城市入边边权加上了 y 的邻接矩阵转移即可。
这个点数是 n+4m 的
冷静分析一下可以发现我们并没有必要拆边,而是可以直接拆点。对于从 u 花 w 天走到 v 这个过程,我们可以看成在 u 待了 w−1 天且只在第一天获得点权,然后在第 w 天到达 v。这样子对于每个点 u 拆成若干个点 ui 表示在 u 待的第 i 天即可,连边类似。这样子点数就是 5n 的了。
如果朴素地实现复杂度是 O((5n)^3klogt) 的
显然过不去。因为我们只需要知道 dpT,1,1,所以我们只需要保留 dpk 的第一行,再预处理 G 的 2k 次幂即可做到 O((5n)3logT+(5n)2klogT)。
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=255; int n,m,T,k,c[N],id[N][6],tot; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); return x*f; } struct f {int t,x,y;} t[N]; bool operator <(f a,f b) {return a.t<b.t;} struct Matrix { ll s[N][N]; Matrix() {memset(s,0xc0,sizeof(s));} ll* operator [](int i) {return s[i];} } q[31]; ll ans[N]; Matrix operator *(Matrix a,Matrix b) { Matrix c; for (int i=1;i<=tot;i++) for (int k=1;k<=tot;k++) { if (a[i][k]<0) continue; for (int j=1;j<=tot;++j) c[i][j]=max(c[i][j],a[i][k]+b[k][j]); } return c; } inline ll Max(ll a,ll b) {return a>b?a:b;} inline void Mul(Matrix q) { ll b[N]; for (int i=1;i<=tot;i++) b[i]=-1e18; for (int k=1;k<=tot;k++) { if (ans[k]<0) continue; for (int j=1;j<=tot;++j) b[j]=Max(b[j],ans[k]+q[k][j]); } for (int i=1;i<=tot;i++) ans[i]=b[i]; } int main() { n=read(),m=read(),T=read(),k=read(); tot=n; for (int i=1;i<=n;i++) c[i]=read(); for (int i=1;i<=n;i++) id[i][0]=i; for (int i=1,u,v,w;i<=m;i++) { u=read(),v=read(),w=read(); for (int j=1;j<w;j++) if (id[u][j]==0) id[u][j]=++tot; for (int j=1;j<w;++j) q[0][id[u][j-1]][id[u][j]]=0; q[0][id[u][w-1]][v]=c[v]; } for (int i=1;i<=k;i++) t[i]=(f){read(),read(),read()}; sort(t+1,t+k+1); t[0]=(f){0,0,0},t[k+1]=(f){T,0,0}; for (int i=1;i<=30;i++) q[i]=q[i-1]*q[i-1]; for (int i=2;i<=tot;i++) ans[i]=-1e18; ans[1]=c[1]; for (int i=1,x;i<=k+1;i++) { x=t[i].t-t[i-1].t; for (int j=30;j>=0;j--) if (x & (1<<j)) Mul(q[j]); ans[t[i].x]+=t[i].y; } if (ans[1]<0) puts("-1"); else printf("%lld\n",ans[1]); return 0; }