20230419 训练记录:dp
Posted Patricky
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20230419 训练记录:dp相关的知识,希望对你有一定的参考价值。
Deque
Alice 和 Bob 玩游戏,轮流从 deque 的头/尾取走一个元素,Alice 拿走的数和为 \\(X\\),Bob 拿走的数和为 \\(Y\\)。Alice 想最大化 \\(X - Y\\) 而 Bob 想最小化 \\(X - Y\\)。问双方都采取最优策略情况下的 \\(X - Y\\) 为多少。
\\(n \\leq 3000; a_i \\leq 10^9\\)
用 \\(f_l, r, 0 / 1\\) 表示 Bob / Alice 先手情况下,在区间 \\([l, r]\\) 的最佳答案,所求即 \\(f_0, n - 1, 1\\)。有:
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main()
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::vector<int> a(n);
for (int &i : a) std::cin >> i;
const ll inf = 1e18;
std::vector f(n, std::vector(n, std::array<ll, 2>inf, inf));
std::cout << [&, dp[&](auto &&self, int l, int r, bool alice) -> ll
if (f[l][r][alice] != inf) return f[l][r][alice];
ll ans = 0;
if (l == r) ans = a[l] * (alice ? 1 : -1);
else if (alice) ans = std::max(self(self, l + 1, r, 0) + a[l], self(self, l, r - 1, 0) + a[r]);
else ans = std::min(self(self, l + 1, r, 1) - a[l], self(self, l, r - 1, 1) - a[r]);
return f[l][r][alice] = ans;
]
return dp(dp, 0, n - 1, 1);
();
return 0;
Candies
求 \\(k\\) 拆成 \\(n\\) 个整数的方案数(整数划分数),但是 \\(0 \\leq x_i \\leq a_i\\)。
\\(n \\leq 100; k \\leq 10^5\\)。
整数划分数
整数划分数其实就是完全背包,有:
f[0] = 1; for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) f[j] += f[j - i];
带上限制,用 \\(f_i, j\\) 表示使用前 \\(i\\) 个满足题意的数组成的和为 \\(j\\) 的方案数,所求即 \\(f_n, k\\)。有:
右侧为一段区间和,考虑使用前缀和优化。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main()
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int &i : a) std::cin >> i;
std::vector f(n + 1, std::vector(k + 1, 0LL)), sf;
const int mod = 1000000007;
f[0][0] = s[0][0] = 1;
for (int i = 1; i <= k; i++)
s[0][i] = (s[0][i - 1] + f[0][i]) % mod;
for (int i = 1; i <= n; i++)
f[i][0] = s[i][0] = 1;
for (int j = 1; j <= k; j++)
if (int ai = a[i - 1]; j <= ai)
f[i][j] = s[i - 1][j];
else
f[i][j] = ((s[i - 1][j] - s[i - 1][(j - ai) - 1]) % mod + mod) % mod;
s[i][j] = (s[i][j - 1] + f[i][j]) % mod;
std::cout << f[n][k] << \'\\n\';
return 0;
Slimes
同合并石子,区间 dp 模板题。满足四边形不等式。
展开代码
#include <bits/stdc++.h>
int read()
int x = 0, f = 1, c = getchar();
for (; !isdigit(c); c = getchar()) if (c == \'-\') f = -1;
for (; isdigit(c); x = x * 10 + c - \'0\', c = getchar());
return x * f;
const int N = 410;
using ll = long long;
const ll inf = 1E18;
int n, a[N], s[N][N];
ll f[N][N], sum[N];
#define w(i, j) sum[j] - sum[i - 1]
int main()
n = read();
for (int i = 1; i <= n; i++)
a[i] = read();
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
s[i][i] = i;
for (int l = 2; l <= n; l++)
for (int i = 1, j = l; j <= n; i++, j++)
f[i][j] = inf;
for (int k = s[i][j - 1]; k <= s[i + 1][j]; k++)
if (f[i][j] > f[i][k] + f[k + 1][j] + w(i, j))
f[i][j] = f[i][k] + f[k + 1][j] + w(i, j);
s[i][j] = k;
printf("%lld\\n", f[1][n]);
return 0;
Matching
\\(n\\) 男 \\(n\\) 女,不同性别之间配对,如果 \\(a_i, j = 1\\) 表示 \\(i\\) 对 \\(j\\) 合适。只能一男一女,问多少合法方案数。
\\(n \\leq 21\\)。
\\(n\\) 很小,考虑枚举子集。\\(f_i, s\\) 表示 \\([1, i]\\) 编号的男女,配对情况为 \\(s\\) 的方案数。则有:
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main()
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::vector a(n, std::vector(n, 0));
for (auto &i : a) for (auto &j : i) std::cin >> j;
std::vector f(n + 1, std::vector(1 << n, 0LL));
f[0][0] = 1;
const int mod = 1000000007;
for (int s = 0; s < 1 << n; s++)
for (int i = 0; i < n; i++) if (f[i][s])
for (int j = 0; j < n; j++) if ((~s >> j & 1) && a[i][j])
(f[i + 1][s | (1 << j)] += f[i][s]) %= mod;
std::cout << f.back().back() << \'\\n\';
return 0;
Indepedent Set
将树的所有节点染成黑白两色,求相邻节点不同时为黑的方案数。
\\(n \\leq 10^5\\)。
乘法原理,一路乘上去,如果当前节点是白色就两种加起来,否则为黑色就乘以儿子涂成白色的方案数。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main()
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::vector g(n, std::vector(0, 0));
for (int i = 1; i < n; i++)
int u, v;
std::cin >> u >> v;
u -= 1, v -= 1;
g[u].push_back(v);
g[v].push_back(u);
const int mod = 1000000007;
std::vector f(n, std::array<int, 2>);
[&, dfs[&](auto &&self, int u, int p) -> void
f[u][0] = f[u][1] = 1;
for (auto v : g[u]) if (v != p)
self(self, v, u);
f[u][0] = (ll) f[u][0] * ((ll) f[v][0] + f[v][1]) % mod;
f[u][1] = (ll) f[u][1] * f[v][0] % mod;
]
dfs(dfs, 0, -1);
();
std::cout << ((ll)f[0][0] + f[0][1]) % mod << \'\\n\';
return 0;
Flowers
给定 \\(\\h\\_n, \\a\\_n\\),求最长子序列 \\(\\p\\_k\\) 使得 \\(h_p_i\\) 上升的最大的 \\(\\sum \\limits_i = 1 ^ k a_p_i\\)。
\\(h_i \\leq n \\leq 2 \\times 10^5; a_i \\leq 10^9\\)。
用 \\(f_i\\) 表示考虑到第 \\(i\\) 个元素结尾的最长上升子序列对应的最大和,则:
查询前缀最大值,此事树状数组能胜任。注意到 \\(h_i\\) 很小,可以用下标表示 \\(h_i\\),值表示 dp 的值。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main()
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::vector h(n, 0), a(n, 0);
std::vector p(n + 1, 0LL);
for (auto &i : h) std::cin >> i;
for (auto &i : a) std::cin >> i;
ll f = 0, ans = 0;
for (int i = 0; i < n; i++)
ll res = 0;
for (int x = h[i] - 1; x; x -= x & -x)
res = std::max(ans, p[x]);
f = res + a[i];
for (int x = h[i]; x <= n; x += x & -x)
p[x] = std::max(p[x], f);
ans = std::max(ans, f);
std::cout << ans << \'\\n\';
return 0;
Walk
求长度为 \\(k\\) 的路径条数。
邻接矩阵 \\(n \\leq 50; k \\leq 10^18\\)。
熟知 Floyd 算法的 \\(f_i, j\\) 循环 \\(k\\) 次表示的是走 \\(k\\) 步的最短路径(这也是矩阵乘法的本质之一)。又因为 \\(f_k = f_1^k\\) 所以这是一道矩乘快速幂模板题。
展开代码
#include <bits/stdc++.h>
using ll = long long;
const int N = 50;
const int mod = 1000000007;
using matrix = std::array<std::array<int, N>, N>;
int n;
matrix operator *(const matrix &lhs, const matrix &rhs)
matrix res;
for (int i = 0; i < n; i++)
for (int k = 0; k < n; k++)
for (int j = 0; j < n; j++)
res[i][j] = ((ll) res[i][j] + (ll) lhs[i][k] * rhs[k][j] % mod) % mod;
return res;
matrix power(matrix x, ll k)
matrix res;
for (int i = 0; i < n; i++) res[i][i] = 1;
for (; k; k /= 2, x = x * x)
if (k & 1) res = res * x;
return res;
int main()
std::cin.tie(nullptr)->sync_with_stdio(false);
ll k;
std::cin >> n >> k;
matrix x;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
std::cin >> x[i][j];
matrix ans = power(x, k);
int res;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
res = ((ll) res + ans[i][j]) % mod;
std::cout << res << \'\\n\';
return 0;
Dailight 训练记录
现场赛记录
19 CCPC 湘潭邀请赛 11/Gold
19 ICPC 西安邀请赛 49/Silver
训练规划:
hl:
1.深入增强图论,数据结构的能力,包括但不限于树形dp,点分治,多写dp,学习斜率优化,四边形优化
2.保证银牌及以下图论,数据结构,dp的通过率,最好可以在十分钟内出思路
3.养成提交前检查代码的习惯,不出现傻逼错误。
4.学习简单数论(逆元,组合数),尤其是数论结合图论的应用
gbs:
1.加快上机 -> 写完代码过样例 这一过程的速度。
2.减少debug占用机时
3.首要任务是增加银牌及以下数论的通过率,包括但不限于打表找规律,基础推公式,其次学习较难的数论。
4.增加用简要语言表述题意的能力
zcz.
1.训练计算几何的能力,保证简单及中等计算几何的通过率
2.多看一些思维题及找规律,推公式,构造题,写代码为次,提出正确思路为主。
3.写题的过程尽量了解ACM的出题套路。
总:
1.所有队员多做思维题,保证脑子灵活度
2.多开训练赛,增加讨论题目的配合能力以及队伍机时调度的能力
经验总结:
1.交之前务必重新审视两边代码保证正确,WA之后直接下机并且打印代码,2WA之后需要队友确认题意并且检查代码
2.签到题签完之后如果过的人数中等且写题的十分自信,可以本题单线程,否则需要其他至少一位队友确认题意并且确认写法才可敲题
3.开局开荒题的队员除非特别有自信且剩余码量不高,否则优先将机子让给队友写签到题以降低罚时
4.非最后十分钟垃圾时间严禁挂机,当前没有题挂在线程上的队员就去读没有读过的题
5.难题采用数论给gbs和zcz讨论,图论数据结构给hl,计算几何给zcz,无法确定类型的读给hl确认类型的战略,确认题是否可做
训练赛记录
The 10th Shandong Provincial Collegiate Programming Contest 11/13 rak3/249
以上是关于20230419 训练记录:dp的主要内容,如果未能解决你的问题,请参考以下文章