1. 程式人生 > 實用技巧 >[NOI2020]製作菜品

[NOI2020]製作菜品

首先可以思考一下暴力,不難發現我們可以令 \(dp_{i, j}\) 表示在第 \(i\) 秒到達 \(j\) 的最大愉悅值,這樣轉移就十分顯然了。那麼如果 \(k = 0\),觀察一下轉移的方程:

\[dp_{i, j} = \max_{k \rightarrow j}\{dp_{i - w_{k, j}, k} + c_j\} \]

如果 \(w_{k, j} = 1\) 那麼這將會是一個經典的可優化問題,並將 \(dp_{i - 1}\) 看作一個矩陣,轉移費用看成一個矩陣 \(\forall j \rightarrow k, val_{j, k} = c_k\),實際上這個過程是一個類似於矩乘的過程,即 \(A \times B(i, j) = \max\limits_{k = 1} ^ {n}\{A_{i, k} + B_{k, j}\}\)

為了能加速轉移,下面我們來證明這個矩乘依然是滿足結合律的。

回憶一下原式矩乘的結合律是怎麼證明的,即我們要證明 \((A \times B) \times C = A \times (B \times C)\) 為了方便起見我們令三個矩陣的大小都為 \(n \times n\) 空的位置用 \(0\) 補齊是不會影響的,那麼有:

\[\begin{aligned} (A \times B) \times C(i, j) &= \sum\limits_{k = 1} ^ n \bigg(\sum\limits_{l = 1} ^ n A_{i, l} \times B_{l, k}\bigg) \times C_{k, j}\\ &= \sum\limits_{l = 1} ^ n \sum\limits_{k = 1} ^ n A_{i, l} \times B_{l, k} \times C_{k, j}\\ &= \sum\limits_{k = 1} ^ n A_{i, k} \sum\limits_{l = 1} ^ n B_{k, l} \times C_{l, j} = A \times (B \times C)(i, j) \end{aligned} \]

從上面的過程中可以看到,矩乘滿足結合律的要求是滿足分配律即 \(\bigg(\sum\limits_{l = 1} ^ n A_{i, l} \times B_{l, k}\bigg) \times C_{k, j}\) 可以將裡面的 \(C_{k, j}\) 乘進去。那麼實際上 \(\max\{a, b\} + c = \max\{a + c, b + c\}\) 也是可以拉進去的,因此上面新定義的這個矩乘運算也是滿足結合律的。所以這個新定義的矩乘也是可以使用矩陣快速冪加速的。於是我們現在的問題在於考慮如何讓邊權變為 \(1\),因為邊權最大隻有 \(5\),於是我們可以將每條邊拆成 \(w - 1\)

個點,依次連起來即可,但是這樣點數是 \(n + 4m\) 的,不足以通過本題。因為點數很小,因此我們可以直接將每個點拆成 \(5\) 個點,然後按照長度直接連邊,將價值掛在最後一個點上即可。這樣點數就是 \(5n\) 的,複雜度 \(O((5n) ^ 3 \log T)\) 可以通過本題。

再考慮 \(k \ne 0\) 的情況,實際上我們發現問題的改變本質在於轉移矩陣在這 \(k\) 個點上變得不同,於是我們直接在這 \(k\) 個位置單獨轉移即可,複雜度 \(O(k(5n) ^ 3 \log T)\) 但是這還是不能過題。但是可以發現的是在本題中我們是用一個向量乘一個矩陣,這個過程是 \(O(n ^ 2)\) 的,並且每次我們單獨轉移的部分實際上只需要 \(dp_{t_i, x_i} += y_i\) 即可,轉移矩陣並沒有變,那麼我們可以事先將轉移矩陣 \(G\)\(G ^ 0, G ^ 2, G ^ 4, \cdots G ^ {2 ^ i}, \cdots\) 預處理出來,每次單獨轉移的部分用向量 \(dp_i\) 去乘每個轉移矩陣 \(G ^ {2 ^ j}\) 即可,那麼這樣預處理的複雜度為 \(O((5n) ^ 3 \log T)\),轉移的複雜度為 \(O(k(5n) ^ 2 \log T)\),就可以通過本題了。

#include<bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for(int i = l; i <= r; ++i)
#define dep(i, l, r) for(int i = r; i >= l; --i)
const int N = 250 + 5;
const long long inf = 100000000000000;
int n, m, T, k, u, v, w, c[N];
long long tmp;
struct mat{
    long long g[N][N];
    void clear(){
        rep(i, 1, n) rep(j, 1, n) g[i][j] = -inf;
    }
}tr, dp, f[N];
struct node{
    int t, x, y;
}a[N];
int read(){
    char c; int x = 0, f = 1;
    c = getchar();
    while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int P(int x, int y){
    return 5 * (x - 1) + y;
}
mat mul(mat a, mat b){
    mat c; c.clear();
    rep(k, 1, n) if(a.g[1][k] >= 0){
        rep(j, 1, n) if(b.g[k][j] >= 0) c.g[1][j] = max(c.g[1][j], a.g[1][k] + b.g[k][j]);
    } 
    return c;
}
mat Mul(mat a, mat b){
    mat c; c.clear(); 
    rep(i, 1, n) rep(k, 1, n) if(a.g[i][k] >= 0){
        rep(j, 1, n) if(b.g[k][j] >= 0) c.g[i][j] = max(c.g[i][j], a.g[i][k] + b.g[k][j]);
    } 
    return c;
}
mat Qpow(mat a, int b){
    mat ans = a; --b; // 注意這裡
    while(b){
        if(b & 1) ans = Mul(ans, a);
        a = Mul(a, a), b >>= 1;
    }
    return ans;
}
bool cmp(node a, node b){
    return a.t < b.t;
}
void solve(int T, int x, int y){
    dep(i, 0, 29) if(T >= (1 << i)) dp = mul(dp, f[i]), T -= (1 << i);
    if(dp.g[1][P(x, 5)] >= 0) dp.g[1][P(x, 5)] += y; 
}
int main(){
    n = read(), m = read(), T = read(), k = read();
    rep(i, 1, n) c[i] = read();
    rep(i, 1, 5 * n) rep(j, 1, 5 * n) tr.g[i][j] = -inf;
    rep(i, 1, n) rep(j, 1, 4) tr.g[P(i, j)][P(i, j + 1)] = (j == 4 ? c[i] : 0);
    rep(i, 1, m) u = read(), v = read(), w = read(), tr.g[P(u, 5)][P(v, 5 - w + 1)] = (w == 1 ? c[v] : 0);
    rep(i, 1, k) a[i].t = read(), a[i].x = read(), a[i].y = read();
    n = 5 * n, f[0] = tr, a[++k].t = T, a[k].x = 1, a[k].y = 0;
    rep(i, 1, 29) f[i] = Mul(f[i - 1], f[i - 1]);
    sort(a + 1, a + k + 1, cmp);
    rep(i, 1, n) dp.g[1][i] = -inf;
    dp.g[1][5] = c[1];
    rep(i, 1, k) solve(a[i].t - a[i - 1].t, a[i].x, a[i].y);
    printf("%lld", dp.g[1][5] >= 0 ? dp.g[1][5] : -1);
    return 0;
}