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;
}