[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^3log⁡k)。

首先,我们把每个点的点权作为它所有入边的边权,再特判起始点的点权(即把 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)^3klog⁡t) 的

显然过不去。因为我们只需要知道 dpT,1,1,所以我们只需要保留 dpk 的第一行,再预处理 G 的 2k 次幂即可做到 O((5n)3log⁡T+(5n)2klog⁡T)。

#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
",ans[1]);
    return 0;
}
原文地址:https://www.cnblogs.com/zzrblogs/p/13534753.html