[NOI2020] 美食家 题解
属于是将矩阵快速幂的绝大部分技巧用到了极致的一道题。
暴力部分
首先我们先考虑一个普通 DP。
定义
显然有
矩阵快速幂&拆点优化
我们先暂时不考虑美食节的情况,这个后面再处理。
观察到这个
我们考虑矩阵快速幂中一个拆点/拆边的技巧。(为啥我不知道捏)
首先一个直观的,对于边
这样你的转移式就变成了
当然这里有个小细节,由于 DP 是求的
但是这样的话,复杂度仍然达到了
我们发现这个
具体来说,我们将点
此时时间复杂度来到
特殊点处理
在进行了进一步的优化之后,此时我们就需要考虑一下美食节的问题了。
由于美食节会将
时间复杂度
向量乘矩阵优化
我们发现我们为了求出
有一个很逆天的东西是,转移矩阵自乘的时间复杂度是
假设转移矩阵是
#include <bits/stdc++.h>
using namespace std;
#define maxn 505
#define ll long long
struct matrix
{
vector<vector<ll> > a;
matrix operator *(const matrix &c)const
{
matrix now;
int n = a.size(), m = c.a.size(), l = c.a[0].size();
now.a.resize(n);
for (int i = 0; i < n; ++i) now.a[i].resize(l);
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < l; ++j)
{
now.a[i][j] = INT_MIN;
for (int k = 0; k < m; ++k)
if(a[i][k] != INT_MIN && c.a[k][j] != INT_MIN)
now.a[i][j] = max(now.a[i][j], a[i][k] + c.a[k][j]);
}
}
return now;
}
matrix(){}
matrix(int len1, int len2, int op = 1)
{
a.resize(len1);
for (int i = 0; i < len1; ++i) a[i].resize(len2);
if(op) for (int i = 0; i < len1; ++i) a[i][i] = 1;
}
};
int n, m, T, k;
int c[maxn], num[maxn][6];
matrix trans[31], Now;
int t[maxn], x[maxn], y[maxn], id[maxn];
bool cmp(int x, int y)
{
return t[x] < t[y];
}
int main()
{
cin >> n >> m >> T >> k;
for (int i = 1; i <= n; ++i) cin >> c[i];
for (int i = 1; i <= n; ++i) for (int j = 1; j <= 5; ++j) num[i][j] = (i - 1) * 5 + j;
matrix now(5 * n, 5 * n), ans(1, 5 * n);
for (int i = 0; i < 5 * n; ++i) ans.a[0][i] = INT_MIN;
ans.a[0][0] = c[1];
for (int i = 0; i < 5 * n; ++i) for (int j = 0; j < 5 * n; ++j) now.a[i][j] = INT_MIN;
for (int i = 1; i <= m; ++i)
{
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
int zd = y;
x = num[x][z], y = num[y][1];
now.a[x - 1][y - 1] = c[zd];
}
for (int i = 1; i <= n; ++i) for (int j = 2; j <= 5; ++j) now.a[num[i][j - 1] - 1][num[i][j] - 1] = 0;
int last = 0;
trans[1] = now;
for (int i = 2; i <= 30; ++i) trans[i] = trans[i - 1] * trans[i - 1];
for (int i = 1; i <= k; ++i) scanf("%d %d %d", &t[i], &x[i], &y[i]), id[i] = i;
stable_sort(id + 1, id + k + 1, cmp);
for (int j = 1; j <= k; ++j)
{
int i = id[j];
Now = now;
for (int j = 0; j < 5 * n; ++j) if(Now.a[j][num[x[i]][1] - 1] != INT_MIN) Now.a[j][num[x[i]][1] - 1] += y[i];
int need = t[i] - 1 - last;
while(need)
{
int x = need & -need;
ans = ans * trans[__lg(x) + 1];
need -= x;
}
ans = ans * Now;
last = t[i];
}
if(last < T)
{
int need = T - last;
while(need)
{
int x = need & -need;
ans = ans * trans[__lg(x) + 1];
need -= x;
}
}
if(ans.a[0][0] == INT_MIN) ans.a[0][0] = -1;
cout << ans.a[0][0] << endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】