Loading

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;
}
posted @ 2022-11-02 19:57  ZCETHAN  阅读(35)  评论(0编辑  收藏  举报