2024 暑假友谊赛-热身2
2024 暑假友谊赛-热身2
A - 🐂
题意
Creatnx 有 \(n\)(\(1 \le n \le 2 \cdot {10}^5\))面魔镜,每天她会问一面镜子:“我漂亮吗?”,第 \(i\) 面镜子有 \(\dfrac{p_i}{100}\)(\(1 \le p_i \le 100\))的概率告诉 Creatnx 她漂亮。
Creatnx 从第 \(1\) 面镜子开始,每天询问一面镜子。对第 \(i\) 面镜子,将会发生两种情况:
- 如果这面镜子告诉 Creatnx 她很漂亮:
- 如果这是第 \(n\) 面镜子,那么 Creatnx 将会十分开心,并且停止询问。
- 反之,Creatnx 将在第二天询问第 \(i+1\) 面镜子。
- 反之,Creatnx 将会十分伤心,第二天重新从询问第 \(1\) 面镜子开始询问。
求 Creatnx 变得开心的期望天数,对 \(998, 244, 353\) 取模。
思路
考虑期望 dp。
设 \(dp_i\) 表示第 1 天到第 i 天变得开心的期望天数,如果第 i 天询问成功,则贡献为 \(p_i(dp_{i-1}+1)\),否则需要回到第一天,这个时候贡献的天数为 \(dp_{i-1}+1\),且他再次回到 i 变得开心的期望天数为 \(dp_i\),所以此时的贡献为\((1-P_i)(dp_{i-1}+1+dp_i)\)
则有:
这里 \(p_i\) 是指的概率,实际计算需除以 100,即:
之后就是线性递推即可。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; const i64 mod = 998244353; i64 ksm(i64 x, i64 y) { i64 res = 1; while (y) { if (y & 1) res = res * x % mod; x = x * x % mod; y >>= 1; } return res; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); i64 n; cin >> n; vector<i64> dp(n + 1), p(n + 1); for (int i = 1; i <= n; i ++) { cin >> p[i]; dp[i] = ((dp[i - 1] + 1) * 100 % mod * ksm(p[i], mod - 2)) % mod; } cout << dp[n] << '\n'; return 0; }
D - 🐂🐂🐂🐂🐂
题意
给一个正整数 N,求满足 \(\frac{4}{N}=\frac{1}{h}+\frac{1}{n}+\frac{1}{w}\) 的任一具体方案,输出 \(h,n,w\)。
思路
枚举两个数,判断另一个数是否符合要求即可。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; const int N = 3500; for (int i = 1; i <= N; i ++) { for (int j = 1; j <= N; j ++) { i64 A = 1ll * i * j * n; i64 B = 4 * i * j - n * (i + j); if (B <= 0) continue; if (A % B == 0) { cout << i << ' ' << j << ' ' << A / B << '\n'; return 0; } } } return 0; }
E - 🐂🐂🐂🐂🐂🐂
题意
Snuke 来到一家商店,那里出售装有球的盒子。 商店出售以下三种包装盒:
- 红盒子,每个盒子包含 \(R\) 个红球
- 绿盒子,每个盒子包含 \(G\) 个绿球
- 蓝盒子,每个盒子包含 \(B\) 个蓝球
Snuke 希望通过购买 \(r\) 红盒子,\(g\) 绿盒子和 \(b\) 蓝色盒子来获得总共 \(N\) 个球。有多少个非负整数对 \((r,g,b)\) 完成此任务?
思路
预处理其中一个盒子能够买的方案数,枚举另外两个盒子,判断剩下的球能否在先前的盒子里买到。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int r, g, b, n; cin >> r >> g >> b >> n; const int N = 3010; vector<int> num(N); int i = 0; while (i * b <= n) { num[i * b] = 1; i ++; } int ans = 0; for (int i = 0; i <= N; i ++) { for (int j = 0; j <= N; j ++) { if (i * r + j * g > n) break; ans += num[n - i * r - j * g]; } } cout << ans << '\n'; return 0; }
F - 🐂🐂🐂🐂🐂🐂🐂
题意
给你 n 个字符串,求将它们任意顺序拼接成一个字符串后中 AB
的最大数量。
思路
统计以 B 开头,以 A 结尾的个数,惯性思维就是这两中的最小值,但是有个很坑的点就是,如果一个字符串以 B 开头的同时又以 A 结尾(以下称BA串),那如果只判断最小值就错了,比如 2 个 BA,其只能合出一个 AB,所以对于这种情况,我们可以用一个 A 结尾的和一个 B 开头把它变成非 BA 串,这样答案贡献就加了 2,且 n 个 BA 只会产生 n-1 个AB 就是了,如 \((B\dots A|B\dots A)\)。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; int pre[30], suf[30], same[30][30]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<string> s(n); for (auto &i : s) cin >> i; int ans = 0; for (auto c : s) { for (int i = 0; i < c.size(); i ++) { if (c.substr(i, 2) == "AB") ans ++; } int f = c[0] - 'A', b = c.back() - 'A'; if (f == 1 && !b) { same[f][b] ++; } else { pre[f] ++, suf[b] ++; } } if (same[1][0]) { if (pre[1] || suf[0]) { pre[1] --, suf[0] --; ans += 2; } same[1][0] --; } cout << ans + min(pre[1], suf[0]) + same[1][0] << '\n'; return 0; }
G - 🐂🐂🐂🐂🐂🐂🐂🐂
题意
给出正整数 𝑛,求出所有的满足 𝑛 除以 𝑚 的商等于 𝑛 除以 𝑚 的余数的 𝑚 的和。
思路
设商为 k ,则余数也为 k,因为被除数 = 商 × 除数 + 余数,所以有 \(n=k\times m+k = k(m+1)\) ,所以枚举 k 即可求出 m,又因为除数 > 余数,即 m > k,所以 \(n=k(m+1)>k(k+1)\) ,所以当 \(k \times (k+1) >n\) 时退出循环即可。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); i64 n; cin >> n; i64 ans = 0; for (i64 i = 1; i * (i + 1) < n; i ++) { if (n % i == 0) ans += (n / i - 1); } cout << ans << '\n'; return 0; }
H - 🐂🐂🐂🐂🐂🐂🐂🐂🐂
题意
已知数列 $ A_1,\ A_2,\ ...,\ A_N $ 和整数 \(K\)。
$ B $ 是 $ A $ 的子序列,相邻任意 $ B $ 的数的差的绝对值都小于等于 $ K $。
输出数列 $ B $ 长度的最大值。
思路
考虑 dp,设 \(dp_i\) 为以第 i 个数结尾的最长子序列长度,则有转移方程为:
复杂度 \(O(n^2)\),且不好优化,则考虑 \(dp_i\) 是以 i 为结尾的最长子序列长度,则转移方程为 :
可以发现,就是区间查询最大值,查询后把对应的 i 的最大子序列长度修改为 \(dp_j+1\) 即是单点修改,没错,这就是线段树。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; #define lc u << 1 #define rc u << 1 | 1 const int N = 3e5 + 10; struct SegmentTree { int l, r, Max; } tr[N << 2]; void pushup(int u) { tr[u].Max = max(tr[lc].Max, tr[rc].Max); } void build(int u, int l, int r) { tr[u] = {l, r, 0}; if (l == r) return; int mid = l + r >> 1; build(lc, l, mid); build(rc, mid + 1, r); pushup(u); } void updata(int u, int pos, int x) { if (tr[u].l == tr[u].r) { tr[u].Max = x; return ; } int mid = tr[u].r + tr[u].l >> 1; if (pos <= mid) updata(lc, pos, x); else updata(rc, pos, x); pushup(u); } int query(int u, int l, int r) { if (l <= tr[u].l && tr[u].r <= r) { return tr[u].Max; } int res = 0; int mid = tr[u].r + tr[u].l >> 1; if (l <= mid) res = max(res, query(lc, l, r)); if (r > mid) res = max(res, query(rc, l, r)); return res; } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, k; cin >> n >> k; const int Maxn = 3e5; build(1, 1, Maxn); for (int i = 1; i <= n; i ++) { int x; cin >> x; int l = max(0, x - k), r = min(Maxn, x + k); updata(1, x, query(1, l, r) + 1); } cout << tr[1].Max << '\n'; return 0; }
本文作者:Ke_scholar
本文链接:https://www.cnblogs.com/Kescholar/p/18300088
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2023-07-13 SMU Summer 2023 Contest Round 3