1. 程式人生 > 其它 >美食家

美食家

Max-add矩陣 拆點思想

$0x00$ 前置知識$max-add$矩陣:

其實可以理解為$floyd$,不過列舉順序按照矩陣乘法列舉,記得賦值初始值的時候別忘記賦$-inf$

然後這個矩陣也滿足矩陣乘法的結合律,因而可以使用快速冪優化遞推

$0x01$ 暴力$dp$:

這個題$dp$很好想了,$dp[i][u]+c[v]=dp[i+w][v]$,第一維是時間,第二維是到那個點

關於美食節的處理就是看看轉移過來的時候那個時間是否在$v$城市過了節,過了節的情況再加上$y[v]$

$0x02$ 矩陣加速:

按照原來的找合法路徑個數的題一樣,使用鄰接矩陣作為轉移矩陣

但是這道題比較煩的事每條邊貢獻的時間是不同的

可以想到和題目 迷路 很相似,可以使用拆點的思想,讓每次轉移層次都加一

我們設$u$拆出來的點(包括他自己)分別為$u_1,u_2,u_3,u_4,u_5$,那麼對於一條邊$(u,v,w)$,

可以拆成兩段,一段是$u_1->u_w$,這一段邊權值都是$0$,第二段是$u_w->v_1$,邊權為$c_v$

這樣會漏一個$1$號點,那麼在答案矩陣上面給$ans[1][1]$賦值$c_1$即可

那麼就可以直接對建好的$5*n$的矩陣轉移$T$次乘上一個答案矩陣,答案就是$ans[1][1]$

這是不考慮美食節的情況。

$0x03$ 美食節處理:

考慮美食節不同時出現,我們將其按時間排序,那麼可以得到$dp[t_i]=dp[t_{i-1}]*G^{t_i-t_{i-1}}+y[x]$

如果直接這麼打,複雜度會超,考慮預處理出$G^{2^i}$,再進行二進位制拆分計算,因為每次計算的$dp[]$是一個向量矩陣

所以最終的時間複雜度為$O((5n)^3\log T+(5n)^2k\log T)$

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 namespace AE86{
 5     inline int read(){
 6         int x=0,f=1;char ch=getchar();
 7         while(ch<'
0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 int n,m,k,T,cnt,id[55][6],c[55],dp[205]; 15 namespace Matrix{ 16 struct Ma{ 17 int s[255][255]; 18 Ma(){memset(s,-0x3f,sizeof(s));} 19 inline void pre(){for(int i=1;i<=cnt;i++)s[i][i]=0;} 20 Ma operator*(const Ma&x)const{ 21 Ma ans; 22 for(int i=1;i<=cnt;i++) 23 for(int j=1;j<=cnt;j++) 24 for(int k=1;k<=cnt;k++) 25 ans.s[i][j]=max(ans.s[i][j],s[i][k]+x.s[k][j]); 26 return ans; 27 } 28 };Ma g; 29 inline Ma ksm(Ma a,int b){ 30 Ma ans; ans.pre(); 31 for(;b;b>>=1,a=a*a) if(b&1) ans=ans*a; 32 return ans; 33 } 34 }using namespace Matrix; 35 Ma bit[30]; 36 struct festival{ 37 int t,x,y; 38 bool operator<(const festival&x)const{ 39 return t<x.t; 40 } 41 }fe[205]; 42 inline void trans(Ma& ans,int b){ 43 for(int i=0;i<30;i++) if(b&(1<<i)){ 44 Ma res; 45 for(int j=1;j<=cnt;j++) 46 for(int k=1;k<=cnt;k++) 47 res.s[1][j]=max(res.s[1][j],ans.s[1][k]+bit[i].s[k][j]); 48 ans=res; 49 } 50 } 51 namespace WSN{ 52 inline short main(){ 53 n=read(); m=read(); T=read(); k=read(); 54 for(int i=1;i<=n;i++){ 55 c[i]=read(); 56 for(int j=1;j<=5;j++) id[i][j]=++cnt; 57 for(int j=1;j<5;j++) g.s[id[i][j]][id[i][j+1]]=0; 58 } 59 for(int i=1,u,v,w;i<=m;i++){ 60 u=read(); v=read(); w=read(); 61 g.s[id[u][w]][id[v][1]]=c[v]; 62 } 63 Ma ans;ans.s[1][id[1][1]]=c[1];bit[0]=g; 64 for(int i=1;i<30;i++) bit[i]=bit[i-1]*bit[i-1]; 65 for(int i=1,t,x,y;i<=k;i++){ 66 t=read(); x=read(); y=read(); 67 fe[i]=(festival){t,x,y}; 68 } sort(fe+1,fe+k+1); 69 int last=0; 70 for(int i=1;i<=k;i++){ 71 trans(ans,fe[i].t-last); 72 if(ans.s[1][id[fe[i].x][1]]>=0) 73 ans.s[1][id[fe[i].x][1]]+=fe[i].y; 74 last=fe[i].t; 75 } 76 if(fe[k].t!=T) trans(ans,T-fe[k].t); 77 write(ans.s[1][id[1][1]]>=0?ans.s[1][id[1][1]]:-1); 78 return 0; 79 } 80 } 81 signed main(){return WSN::main();}
美食家