P6772 [NOI2020] 美食家
题意
给定一张有向图,点有点权,边有边长。现在你从 \(1\) 号点出发,经过时间 \(T\) 之后恰好回到 \(1\),期间每次经过每一个点都可以获得它的点权。其中,在不同的 \(k\) 次时间某些节点会能获得额外的权值。求这 \(T\) 时间能获得的最大权值和。
Solution
首先不考虑额外的权值。考虑到 \(n\) 和 \(w_i\) 都很小,\(T\) 又那么大,很自然想到矩阵优化 dp。令 \(dp_{t,i}\) 表示在第 \(t\) 时间在节点 \(i\),所能获得的最大价值。这样我们需要一个 \(50\times 5=250\) 的矩阵。因为每个节点可能从 \(t-5\) 的节点转移过来。这样矩阵加速一下就是 \(250^3\times \log T\),信仰一手应该能过。
然后考虑对每一个能获得额外权值的时间分段,每段快速幂,然后到那时间手动乘一次,接下去再快速幂。
好像是个无脑题啊。
信仰结束,75pts TLE。
\(\bigstar\) 如果在矩阵乘法的时候被卡了,那么考虑预处理转移矩阵的二次幂,然后每次直接拿向量去乘。这时候分段的复杂度就被平衡掉了。
Code
// Problem:
// P6772 [NOI2020] 美食家
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6772
// Memory Limit: 512 MB
// Time Limit: 2000 ms
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
#define int long long
using namespace std;
int SIZ;
struct Mat{
int a[255][255];
Mat(){memset(a,-0x3f,sizeof(a));}
int* operator[](int x){return a[x];}
Mat friend operator*(Mat m1,Mat m2){
Mat ret;rep(j,1,SIZ) rep(k,1,SIZ) if(m2[k][j]>=0) rep(i,1,SIZ)
ret[i][j]=max(ret[i][j],m1[i][k]+m2[k][j]); return ret;
}
}pw[32];
int c[55];
struct fest{
int t,x,y;void input(){cin>>t>>x>>y;}
bool friend operator<(fest a,fest b){return a.t<b.t;}
}f[210];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,m,T,K;
cin>>n>>m>>T>>K;
rep(i,1,n) cin>>c[i];
Mat op;SIZ=5*n;
rep(i,1,m){
int u,v,w;
cin>>u>>v>>w;
op[v][u+(w-1)*n]=c[v];
}rep(i,n+1,SIZ) op[i][i-n]=0;
pw[0]=op;
rep(i,1,31) pw[i]=pw[i-1]*pw[i-1];
rep(i,1,K) f[i].input();
sort(f+1,f+1+K);
int lst=1;
Mat dp;dp[1][1]=c[1];
rep(i,1,K){
int t=f[i].t-lst;
rep(j,0,31) if((t>>j)&1) dp=pw[j]*dp;
rep(j,1,SIZ) op[f[i].x][j]+=f[i].y;
dp=op*dp;
rep(j,1,SIZ) op[f[i].x][j]-=f[i].y;
lst=f[i].t+1;
}
rep(j,0,31) if(((T-lst+1)>>j)&1) dp=pw[j]*dp;
if(dp[1][1]<0) cout<<-1<<'\n';
else cout<<dp[1][1]<<'\n';
return 0;
}