Loading

NOI2020 Day1

美食节

美食节比较麻烦,先不考虑它,即考虑 \(k = 0\) 的情况。

\(f_{i, u}\) 表示第 \(i\) 天在 \(u\) 的最大愉悦值。

转移:

\[f_{i, v} \gets f_{i - w_{u, v}, u} + c_v \]

初值 \(f_{0, 1} = c_1\),答案 \(f_{T, 1}\)

时间复杂度 \(\mathcal O(Tm)\)

看到这个巨大的 \(T\),结合 DP,容易联想到矩阵乘法。但是很不好的一点是大多数时候我们并不会从 \(f_i\) 转移到 \(f_{i + 1}\),而是 \(f_{i + w}\),这是矩阵乘法不好做的。

注意到 \(w\) 奇小,所以考虑重新设计状态,拆点,设 \(f_{i, j, u}\) 表示现在是第 \(i\) 天,还有 \(j\) 天才能够到达 \(u\)

转移:

\[f_{i + 1, w_{u, v} - 1, v} \gets f_{i, 0, u} + c_v \\ f_{i + 1, j - 1, u} \gets f_{i, j, u} \]

初值 \(f_{0, 0, 1} = c_1\),答案 \(f_{T, 0, 1}\)

由转移可以看出 \(j \le 4\),所以一拆五。

时间复杂度 \(\mathcal O(n^3 \log T)\),但是因为拆点带了个 \(125\) 倍常数。

这就足够拿满 \(k = 0\) 的所有点了,继续考虑 \(k > 0\) 的情况:

其实 \(k\) 也不大,所以我们完全可以把时间分为至多 \(k + 1\) 段,每段分别转移,时间复杂度 \(\mathcal O(kn^3\log T)\),但是常数会变小不少,因为 \(\log T\) 是完全跑不满的。

考虑优化矩阵快速幂,每次求转移矩阵的 \(2\) 的幂的时间复杂度时 \(\mathcal O(n^3 \log T)\),把向量和转移矩阵乘起来求答案的时间复杂度是 \(\mathcal O(n^2 \log T)\),把前一部分单拿出来预处理,快速幂时直接乘就好。这样时间复杂度达到 \(\mathcal O((n^3 + kn^2) \log T)\),过过过。

代码
#include <bits/stdc++.h>

using namespace std;

using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using lll = __int128;
using ld = long double;

template<typename tp> inline void chkmax(tp &x, tp y) {x = max(x, y);}
template<typename tp> inline void chkmin(tp &x, tp y) {x = min(x, y);}

constexpr int N = 60, logT = 30, K = 210;

int n, m, T, k, c[N];

struct Matrix {
    ll a[5 * N][5 * N];

    Matrix() {memset(a, 0xaf, sizeof(a));}
} trans[logT];

Matrix mul(const Matrix &A, const Matrix &B, int n, int r, int m) {
    Matrix res;
    for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) {
        for (int k = 1; k <= r; k++) if (A.a[i][k] >= 0 && B.a[k][j] >= 0) {
            chkmax(res.a[i][j], A.a[i][k] + B.a[k][j]);
        }
    }
    return res;
}

struct Festival {
    int t, x, y;

    bool operator<(const Festival &rhs) const {return t < rhs.t;}
} f[K];

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> m >> T >> k; int n5 = 5 * n;
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
        for (int j = 4; j; j--) trans[0].a[i + n * j][i + n * (j - 1)] = 0;
    }
    for (int u, v, w; m--;) {
        cin >> u >> v >> w;
        trans[0].a[u][v + (w - 1) * n] = c[v];
    }
    int bits = 0;
    for (bits = 1; (1 << bits) <= T; bits++) trans[bits] = mul(trans[bits - 1], trans[bits - 1], n5, n5, n5);
    for (int i = 1; i <= k; i++) cin >> f[i].t >> f[i].x >> f[i].y;
    sort(f + 1, f + k + 1);
    Matrix ans; ans.a[1][1] = c[1];
    for (int i = 1; i <= k; i++) {
        int t = f[i].t - f[i - 1].t;
        for (int e = bits; e >= 0; e--) if ((t >> e) & 1) ans = mul(ans, trans[e], 1, n5, n5);
        if (ans.a[1][f[i].x] >= 0) ans.a[1][f[i].x] += f[i].y;
    }
    if (f[k].t < T) {
        int t = T - f[k].t;
        for (int e = bits; e >= 0; e--) if ((t >> e) & 1) ans = mul(ans, trans[e], 1, n5, n5);
    }
    cout << max(ans.a[1][1], -1ll);
    return 0;
}

命运

题意:给定一棵 \(n\) 个点的树和树上的 \(m\) 条链 \(u_i \to v_i\)(可能有重复),保证 \(u_i\)\(v_i\) 的祖先。现在给树上的每条边赋一个 \(0/1\) 中的权值,满足每条链上至少有一条边的权值为 \(1\) 的方案数。

记以 \(u\) 为根的子树为 \(\mathcal T_u\)

树形 DP,只考虑下端点在 \(\mathcal T_u\) 中的链。

链有两种类型:

  • 下端点在 \(\mathcal T_u\) 中,上端点在 \(1 \to fa_u\) 上。

  • 两个端点都在 \(\mathcal T_u\) 中。

对于第一种类型的链,一个性质是若存在两条链 \(x_1 \to y_1\)\(x_2 \to y_2\),且满足 \(x_1, x_2 \in 1 \to fa_u\)\(y_1, y_2 \in \mathcal T_u\)\(dep_{x_1} > dep_{x_2}\),则如若 \(x_1 \to y_1\) 合法,\(x_2 \to y_2\) 一定合法。

\(f_{u, d}\) 表示至少下端点在 \(\mathcal T_u\) 内的链中不合法的上端点中深度最大的为 \(d\)\(\mathcal T_u\) 内的方案数。

转移考虑子结点 \(v\)\(u\) 合并:

\[f_{u, d} \gets f_{u, d} \times \left(\sum_{i = 0}^{dep_u}f_{v, i} + \sum_{i = 0}^d f_{v, i} \right) + f_{v, d} \times \sum_{i = 0}^{d - 1} f_{u, i} \]

然后前缀和优化一下,记 \(s_{u, d} = \sum\limits_{i=0}^d f_{u, d}\),则转移还可以写成:

\[f_{u, d} \gets f_{u, d} \times (s_{v, dep_u} + s_{v, d}) + f_{v, d} \times s_{u, d - 1} \]

时间复杂度 \(\mathcal O(n^2)\)

我们并不会去给 \(1 \to fa_u\) 上的边赋值,所以有值的 \(f_{u, d}\) 的个数不会超过 \(sz_u\),用线段树合并来维护 \(f_u\),时间复杂度 \(\mathcal O(n \log n)\)

代码

时代的眼泪

咕咕咕……

posted @ 2024-06-20 14:13  Chy12321  阅读(14)  评论(0编辑  收藏  举报