2024 暑假友谊赛-热身2

2024 暑假友谊赛-热身2

A - 🐂

CodeForces - 1265E

题意

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)\)

则有:

\[dp_i=p_i(dp_{i-1}+1)+(1-p_i)(dp_{i-1}+1+dp_i) \\ \]

这里 \(p_i\) 是指的概率,实际计算需除以 100,即:

\[dp_i=\frac{p_i}{100}(dp_{i-1}+1)+(1-\frac{p_i}{100})(dp_{i-1}+1+dp_i) \\ \]

\[p_idp_i=100dp_{i-1}+100\\ \]

\[dp_i=\frac{100(dp_{i-1}+1)}{p_i} \]

之后就是线性递推即可。

代码

#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 - 🐂🐂🐂🐂🐂

AtCoder - tenka1_2017_c

题意

给一个正整数 N,求满足 \(\frac{4}{N}=\frac{1}{h}+\frac{1}{n}+\frac{1}{w}\) 的任一具体方案,输出 \(h,n,w\)

思路

枚举两个数,判断另一个数是否符合要求即可。

\[\frac{4}{N}=\frac{1}{h}+\frac{1}{n}+\frac{1}{w}\\ \]

\[\frac{1}{w}=\frac{4}{N}-\frac{1}{h}-\frac{1}{n}\\ \]

\[\frac{1}{w}=\frac{4hn-N(h+n)}{Nhn}\\ \]

\[w=\frac{Nhn}{4hn-N(h+n)}\\ \]

代码

#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 - 🐂🐂🐂🐂🐂🐂

AtCoder - diverta2019_b

题意

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 - 🐂🐂🐂🐂🐂🐂🐂

AtCoder - diverta2019_c

题意

给你 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 - 🐂🐂🐂🐂🐂🐂🐂🐂

AtCoder - diverta2019_d

题意

给出正整数 𝑛,求出所有的满足 𝑛 除以 𝑚 的商等于 𝑛 除以 𝑚 的余数的 𝑚 的和。

思路

设商为 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 - 🐂🐂🐂🐂🐂🐂🐂🐂🐂

AtCoder - abl_d

题意

已知数列 $ A_1,\ A_2,\ ...,\ A_N $ 和整数 \(K\)
$ B $ 是 $ A $ 的子序列,相邻任意 $ B $ 的数的差的绝对值都小于等于 $ K $。
输出数列 $ B $ 长度的最大值。

思路

考虑 dp,设 \(dp_i\) 为以第 i 个数结尾的最长子序列长度,则有转移方程为:

\[dp_i=max_{j=1}^{i-1}([|a_i-a_j|≤k]dp_j+1) \]

复杂度 \(O(n^2)\),且不好优化,则考虑 \(dp_i\) 是以 i 为结尾的最长子序列长度,则转移方程为 :

\[dp_i=max_{j=max(0,a_i-k)}^{min(3e5,a_i+k)}(dp_j+1) \]

可以发现,就是区间查询最大值,查询后把对应的 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;
}
posted @ 2024-07-13 14:53  Ke_scholar  阅读(5)  评论(0编辑  收藏  举报