1. 程式人生 > 實用技巧 >[NOI2020]美食家

[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\n",ans[1]); return 0; }