暑训#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}\) 表示经过某状态的概率,可得方程:
设 \(S_j = \sum_{i = 0}^{n - 1}P_{i, j}\),由于 \(P_{i, j} = pP_{i - 1, j} + (1 - p)P_{i, j - 1}\) , 累加可得:
整理之后可得:
再结合 \(P_{n - 1, j}\) 的递推式:
就可以通过递推得到 \(S_j\) ,而对于每个 \(z\) ,这一部分的答案就是 \(\sum_{j = 0}^{z}S_j\)。
再考虑 \(b = 0\) 的情形。这一部分的期望尝试次数是好计算的。
设从 \(a\) 从 \(0\) 到 \(i\) 期望尝试次数为 \(D_i\) ,从 \(i - 1\) 到 \(i\) 的期望尝试次数为 \(d_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;
}

第五场因为天气是在宿舍打的,太变态了这场。第六场基本是way在补,开摆。
浙公网安备 33010602011771号