暑训#5#6 补题

5H

题目大意:
给定 \(n, m, s, t, \{a_n\},\{k_n\},\{b_n\},\{c_n\}\)
每次操作,选定任一非 \(0\)\(b_i \to \max\{b_i - p, 0\}\) ,若此后 \(b_i=0\)\(a_i\to \max\{a_i - c_i, 0\}\).
然后选择一项:

  • 使第一个非 \(0\)\(a_i \to \max\{a_i - m, 0\}\)。若此后 \(a_i=0\)\(m\to m+k_i\).
  • \(cnt \to cnt + m\)\(cnt\) 初始为 \(0\)
    \(\min p\) ,使得存在一种方案使得 \(t\) 次操作内可能使 \(cnt\geq s\).

知识点: 二分,dp

这题的难度相当一部分来自于冗长的题面,我费了老大劲才把原题面中 Civilization VI 的元素换成了数学语言。赛时也是被这题面吓到以为是大模拟。

首先显然可以二分答案,将问题转化成给定 \(p\) ,判定方案存在性。

考虑 \(dp\) 来做。令 \(dp_{i,j,l}\) 表示前 \(i\) 次操作中,已经使 \(a_j=0\),选择了 \(l\)\(b_{\{j + 1...n\}}\) 中元素的情况下 \(cnt\) 的最大值。

\(sum_i = m+\sum_{j=1}^{i}k_j\) ,有如下三种转移:

  • \(i\) 次操作选择增加 \(cnt\)

    \[dp_{i,j,l}+sum_j\to dp_{i+1,j,l+1} \]

  • 通过若干次操作使 \(a_{j+1}\to0\)(未触发 \(a_i\to \max\{a_i - c_i, 0\}\)):

    \[dp_{i,j,l}\to dp_{i+\left \lceil \frac{a_{j+1}}{sum_j} \right \rceil,j+1, l+\left \lceil \frac{a_{j+1}}{sum_j} \right \rceil} \]

  • 通过若干次操作使 \(a_{j+1}\to0\)(触发 \(a_i\to \max\{a_i - c_i, 0\}\)):

    \[dp_{i,j,l}\to dp_{i+ \left \lceil \frac{a_{j+1} - c_{j+1}}{sum_j} \right \rceil,j+1,l-\left \lceil \frac{b_{j+1}}{p} \right \rceil + \left \lceil \frac{a_{j+1} - c_{j+1}}{sum_j} \right \rceil} \]

    其中 \(l-\left \lceil \frac{b_{j+1}}{p} \right \rceil + \left \lceil \frac{a_{j+1} - c_{j+1}}{sum_j} \right \rceil \geq 0\)

存在某 \(dp_{i,j,l}\geq s\) 则方案存在。

注意写好转移时的边界条件,并特判 \(p=0\) 的情况即可。

时间复杂度 \(\mathcal O(t^2n\log b)\) ,可以通过本题。

Code:

#include <bits/stdc++.h>
using namespace std;

#define FOR(i,a,b) for (int i = a; i <= b; i++)
#define ceiL(x, y) ((x - 1) / y + 1)
#define int long long

const int N = 105;
const int INF = LONG_LONG_MAX;

int n, m, s, t;
int sumk[N];

int a[N], k[N], b[N], c[N];

int dp[N][N][N];

bool check(int x) {
    FOR(i, 0, t) FOR(j, 0, n) FOR(l, 0, t) dp[i][j][l] = -INF;
    dp[0][0][0] = 0;
    FOR(i, 0, t) {
        FOR(j, 0, n) {
            FOR(l, 0, i) {
                if (dp[i][j][l] == -INF) continue;
                if (dp[i][j][l] >= s) return 1;
                if (i + 1 < N && l + 1 < N) dp[i + 1][j][l + 1] = max(dp[i + 1][j][l + 1], dp[i][j][l] + sumk[j]);
                if (i + ceiL(a[j + 1], sumk[j]) < N && j + 1 < N && l + ceiL(a[j + 1], sumk[j]) < N)
                    dp[i + ceiL(a[j + 1], sumk[j])][j + 1][l + ceiL(a[j + 1], sumk[j])] 
                    = max(dp[i + ceiL(a[j + 1], sumk[j])][j + 1][l + ceiL(a[j + 1], sumk[j])], dp[i][j][l]);
                int newl = l - ceiL(b[j + 1], x) + ceiL(a[j + 1] - c[j + 1], sumk[j]);
                if (i + ceiL(a[j + 1] - c[j + 1], sumk[j]) < N && j + 1 < N && newl >= 0 && newl < N) 
                    dp[i + ceiL(a[j + 1] - c[j + 1], sumk[j])][j + 1][newl]
                    = max(dp[i][j][l], dp[i + ceiL(a[j + 1] - c[j + 1], sumk[j])][j + 1][newl]);
            }
        }
    }
    return 0;
}

signed main() {
    cin >> m >> s >> t;
    cin >> n;
    FOR(i, 1, n) cin >> a[i] >> k[i] >> b[i] >> c[i];
    sumk[0] = m;
    FOR(i, 1, n) sumk[i] = sumk[i - 1] + k[i];
    FOR(i, 0, N - 1) FOR(j, 0, N - 1) FOR(k, 0, N - 1) dp[i][j][k] = -INF;
    // 先判断 p = 0 行不行,只有前两种转移
    dp[0][0][0] = 0;
    FOR(i, 0, t) {
        FOR(j, 0, n) {
            FOR(l, 0, i) {
                if (dp[i][j][l] == -INF) continue;
                if (dp[i][j][l] >= s) { cout << 0 << '\n'; return 0; }
                if (i + 1 < N && l + 1 < N) dp[i + 1][j][l + 1] = max(dp[i + 1][j][l + 1], dp[i][j][l] + sumk[j]);
                if (i + ceiL(a[j + 1], sumk[j]) < N && j + 1 < N && l + ceiL(a[j + 1], sumk[j]) < N)
                    dp[i + ceiL(a[j + 1], sumk[j])][j + 1][l + ceiL(a[j + 1], sumk[j])] 
                    = max(dp[i + ceiL(a[j + 1], sumk[j])][j + 1][l + ceiL(a[j + 1], sumk[j])], dp[i][j][l]);
            }
        }
    }
    int L = 1, R = 1e5 + 10;
    while (L < R) {
        int mid = (L + R) / 2;
        if (check(mid)) R = mid;
        else L = mid + 1;
    }
    if (!check(L)) cout << "-1\n";
    else cout << L << '\n';
    return 0;
}

5L

题目大意:
现有 \(a=0\)\(b=z\),目标为使 \(a=n\)。你每次尝试有 \(p\) 的概率成功使 \(a\to a + 1\) ,另有 \(1−p\) 的概率失败。
失败时,若 \(b>0,b\to b-1\),否则 \(a\to 0\)。对于每个 \(z \in[0,m]\),求期望尝试次数。

知识点: 期望dp

将总期望尝试次数拆成 \(b > 0\)\(b = 0\) 两部分(可能在第一部分中已经达成目标,那么第二部分就没有被经过)

先考虑 \(b > 0\) 的情形。考虑二维状态 \((i, j)\) ,表示当前 \(a = i\)\(b = z - j\)。初始状态为 \((0,0)\) ,目标状态为 \((n,?)\) 。状态转移为:有 \(p\) 的概率 \((i,j)\to (i+1,j)\)\((1 - p)\) 的概率 \((i,j) \to (i, j + 1)\)。由于不会走回头路,第一部分的期望步数就是所有状态的概率之和。

假设 \(P_{i,j}\) 表示经过某状态的概率,可得方程:

\[P_{i,j}=\begin{cases} 1 &\text {if } i, j = 0 \\ p P_{i - 1, j} &\text{if } j = 0 \\ (1-p)P_{i, j - 1} & \text{if } i = 0 \\ pP_{i - 1,j}+(1 - p)P_{i,j - 1} & \text{if } i, j\neq 0 \end{cases} \]

\(S_j = \sum_{i = 0}^{n - 1}P_{i, j}\),由于 \(P_{i, j} = pP_{i - 1, j} + (1 - p)P_{i, j - 1}\) , 累加可得:

\[\sum_{i = 0} ^ {n - 1} P_{i, j} = p\sum_{i = 0} ^{n - 1}P_{i - 1, j} + (1 - p) \sum_{i = 0}^{n - 1} P_{i, j - 1} \]

整理之后可得:

\[S_j = p(S_j - P_{n - 1, j} + P_{-1, j})+(1 - p)S_{j - 1} \]

\[S_j = S_{j - 1}-\frac p {1 - p} P_{n - 1, j} \]

\[S_0 = \frac{1 - p^n}{1 - p} \]

再结合 \(P_{n - 1, j}\) 的递推式:

\[P_{n - 1, j} = C_{n - 1 + j}^{j}p^{n - 1}(1 - p)^{j} = \frac{(n - 1 + j)(1 - p)}{j} P_{n - 1, j - 1} \]

就可以通过递推得到 \(S_j\) ,而对于每个 \(z\) ,这一部分的答案就是 \(\sum_{j = 0}^{z}S_j\)

再考虑 \(b = 0\) 的情形。这一部分的期望尝试次数是好计算的。

设从 \(a\)\(0\)\(i\) 期望尝试次数为 \(D_i\) ,从 \(i - 1\)\(i\) 的期望尝试次数为 \(d_i\) ,则可以得到以下式子:

\[\begin{align} \begin{cases} d_i = p + (1 - p) (D_i+1) \\ d_i = D_i - D_{i - 1} \end{cases} \end{align} \]

可以解得:

\[pD_i = D_{i - 1} + 1 \\ p(D_i + \frac{1}{1-p}) = D_{i - 1} +\frac{1}{1 - p} \\ D_i = \frac{1 - p^i}{(1 - p)p^i} \]

而进入第二部分的概率就是是简单的 \(S_z(1-p)\) ,将概率和期望尝试次数相乘即为第二部分的答案。

总体上时间复杂度为 \(\mathcal O (m\log m)\) ,可通过预处理逆元优化为线性。

Code:

cin >> n >> m >> p;
if (p == 1) {
	FOR(i, 0, m) cout << n % MOD << " ";
    return 0;
}
pbex = qp(inv(p), n) - 1ll;
pp = qp(p, n - 1ll);
ans = sum = (qp(p, n) + MOD - 1ll) * inv(p + MOD - 1ll) % MOD;
cout << (ans + pbex * sum % MOD) % MOD << " ";
FOR(i, 1, m) {
    pp = pp * (n - 1ll + i) % MOD * inv(i) % MOD * (MOD + 1ll - p) % MOD;
    sum = (sum + pp * (MOD - p) % MOD * inv(MOD + 1ll - p) % MOD) % MOD;
    ans = (ans + sum) % MOD;
    cout << (ans + pbex * sum % MOD) % MOD << " ";
}

6E

题目大意:
给定 \(a\)\(p\) 两个数组,在线处理 \(q\) 个操作,操作有两种类型:

  • 给定 \(l\)\(r\)\(x\)。使 \(a_i \to a_i + x (i\in [l, r])\)
  • 给定 \(l\)\(r\)。 计算 \(\sum_{i = l} ^r a_{p_i}\)

知识点: 分块

考虑朴素的分块。记块长为 \(L\)\(a\) 的第 \(i\) 个块为 \(A_i\)\(a_p\) 的第 \(i\) 个块是 \(B_i\)

再记 \(A_i\) 整块加一后 \(B_j\) 的增量为 \(f_{i, j}\) 。并对 \(f_i\) 做前缀和 \(S_{i, j}\) 。(可以在预处理时 \(\mathcal O(n + (\frac{n}{L})^2)\) 求得)

修改涉及整块时,对 \(A_i\) 维护一个懒标记 \(tag_i\) ,则其对 \(B_{l..r}\) 的影响之和为 \(tag_i \sum_{j = l}^{r}f_{i,j}\) ,即 \(tag_i (S_{i, r} - S_{i, l - 1})\) 。至于不成整块的部分,直接暴力修改。单次修改时间复杂度为 \(\mathcal O(\frac nL + L)\)

查询时类似。整块用懒标记统计增量,不成整块的部分直接暴力统计。单次修改时间复杂度也为 \(\mathcal O(\frac n L + L)\)

\(L = \sqrt n\),则总时间复杂度为 \(\mathcal O(n + q\sqrt n)\) ,可以通过本题。

Code:

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define FOR(i,j,k) for (int i = j; i <= k; i++)
#define pii pair<int, int>
#define pb(x) push_back(x)

const int N = 1e5 + 10;
const int B = 320;

int n, m, k, q;
int op, l, r, x, ans;

int a[N], ap[N], c[N], p[N], bg[N], ed[N];
int lz[B + 10], f[B + 10][B + 10], sum[B + 10][B + 10];

vector<int> vc[N];

signed main() {
    cin >> n >> q;
    m = (n - 1) / B + 1;
    FOR(i, 1, n) {
        cin >> p[i];
        vc[p[i]].pb(i);
        c[i] = (i - 1) / B + 1;
        if (i % B == 1) bg[c[i]] = i;
        else if (i % B == 0 || i == n) ed[c[i]] = i;
    }
    FOR(i, 1, n) f[c[p[i]]][c[i]] += 1;
    FOR(i, 1, m) FOR(j, 1, m) sum[i][j] = sum[i][j - 1] + f[i][j];
    while (q--) {
        cin >> op >> l >> r; l ^= ans; r ^= ans;
        if (op == 1) {
            cin >> x; x ^= ans;
            if (c[l] == c[r]) {
                FOR(i, l, r) {
                    a[i] += x;
                    for (int j : vc[i]) ap[c[j]] += x;
                }
            } else {
                FOR(i, c[l] + 1, c[r] - 1) lz[i] += x;
                FOR(i, l, ed[c[l]]) {
                    a[i] += x;
                    for (int j : vc[i]) ap[c[j]] += x;
                }
                FOR(i, bg[c[r]], r) {
                    a[i] += x;
                    for (int j : vc[i]) ap[c[j]] += x;
                }
            }
        } else {
            int ret = 0;
            if (c[l] == c[r]) FOR(i, l, r) ret += a[p[i]] + lz[c[p[i]]];
            else {
                FOR(i, c[l] + 1, c[r] - 1) ret += ap[i];
                FOR(i, 1, m) ret += lz[i] * (sum[i][c[r] - 1] - sum[i][c[l]]);
                FOR(i, l, ed[c[l]]) ret += a[p[i]] + lz[c[p[i]]];
                FOR(i, bg[c[r]], r) ret += a[p[i]] + lz[c[p[i]]];
            }
            cout << ret << '\n';
            ans = ret;
        }
    }
    return 0;
}
posted @ 2025-08-22 20:12  istina  阅读(15)  评论(0)    收藏  举报