zoukankan      html  css  js  c++  java
  • ACM训练赛:第20次

    这次的题思维都很强,等之后的考试结束会集中精力重新训练一些思维题。

    A - A simple question

    CodeForces - 520B

    思路:

    直接看的话,很容易发现如果 (n >= m) 的话 (sum = n - m) 即可,但反过来其实 (m) 推导 (n) 更简单(WA几发后才发现。。)

    如果 (m) 为偶数的话 缩小一半,不然的话先变为偶数再除以2。这样一定能变为 (n)

    void solve() {
        int n, m;
        cin >> n >> m;
        int sum = 0;
        if (n >= m)
            sum = n - m;
        else {
            while (n != m) {
                if (m % 2 == 0 && m > n)
                    m = m / 2, sum++;
                else if (n >= m) {
                    sum = sum + n - m;
                    break;
                }
                if (m % 2 != 0 && m > n)
                    m = (m + 1) / 2, sum = sum + 2;
            }
        }
        cout << sum << endl;
    }
    

    B - Game

    Gym - 102822G (出处2020 CCPC绵阳站)

    题解思路参考:

    看起来是博弈对吧?对,的确是使用SG函数,但蒟蒻表示不会,只能一个个模拟情况,赛后看了下这道题的官方题解(没想到也还是一道模拟找规律的题2333)

    官方题解截图

    image-20201209184114610

    // 这里用的是解法二
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll c0, c1, c2, c3;
    int Case = 1;
    int check() {
        if (c0 == 0 && c1 == c0 && c2 == 0 && c3 == 0)
            return 0;
        int f = 1;
        if (c3 == 0) {
            if (c1 == 0 && c2 == 0) {
                if (c0 == 0 || c0 % 2)
                    f = 0;
            } else {
                if (c0 % 2 == 0) {
                    if (c1 % 3 == 0)
                        f = 0;
                    else {
                        if (c1 % 3 == 1 && c2 == 0)
                            f = 0;
                    }
                } else {
                    if (c1 % 3 == 1 && c2 > 0)
                        f = 0;
                    else if (c1 % 3 == 2 && c2 <= 1)
                        f = 0;
                }
            }
        } else {
            if (c0 % 2 == 0) {
                if (c1 % 3 == 0)
                    f = 0;
                else if (c1 % 3 == 1 && c2 == 0)
                    f = 0;
            } else {
                if (c1 % 3 == 1 && c2 > 0)
                    f = 0;
                else if (c1 % 3 == 2 && c2 <= 1)
                    f = 0;
            }
        }
        return f;
    }
    void solve() {
        cin >> c0 >> c1 >> c2 >> c3;
        int Win_one = check();
        cout << "Case #" << Case++ << ": ";
        if (Win_one == 1)
            cout << "Rabbit
    ";
        else
            cout << "Horse
    ";
    }
    int main() {
        // freopen("in.txt", "r", stdin);
        ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
        int _;
        cin >> _;
        while (_--)
            solve();
    }
    

    看博客的时候发现有人用DFS写了下,感觉也行。下面贴代码

    Code
    int dfs(int c0, int c1, int c2, int c3) {
        if (c0 == 0 && c1 == 0)
            return 0;
        if (c0 == 0 && c2 == 0 && c1 < 2)
            return 0;
        if (c0 > 0 && !dfs(c0 - 1, c1, c2, c3))
            return 1;
        if (c1 >= 2 && !dfs(c0, c1 - 2, c2 + 1, c3))
            return 1;
        if (c1 > 0 && c2 > 0 && !dfs(c0, c1 - 1, c2 - 1, c3 + 1))
            return 1;
        return 0;
    }
    int main() {
        int t;
        scanf("%d", &t);
        int cas = 0;
        while (t--) {
            int a, b, c, d;
            scanf("%d%d%d%d", &a, &b, &c, &d);
            int win = -1;
            if (b == 0 && c == 0 && d == 0) {
                if (a == 0)
                    win = 0;
                else if (a & 1)
                    win = 0;
                else
                    win = 1;
            } else
                win = dfs(a % 2, b % 3, c, d);
            printf("Case #%d: %s
    ", ++cas, win ? "Rabbit" : "Horse");
        }
        return 0;
    }
    

    C - CCCCC

    题目链接: Gym - 102798D (出处:2020 CCPC威阳站)

    思路图来自南京大学题解 ↓(感谢分享)

    题意:给定一个数 C((1 sim 1e18)),找到一组 (a,b) 使得 (a + b = c 且 abc 的素数因子要求 < C) ,如果存在这样的 (a,b) 的话输出 yes,不然输出 no

    其实上方的图片中提到的解决方法已经很好了,现在翻译一下 ↓

    解决方法(翻译):

    • 如果 c 无平方因子,那么是不会存在任意一组 a,b的,即可以直接输出 no
    • 如果 c 包含平方因子,即 :$c = p^2q (p > 1) $ ,所以可以令 (a = pq) , (b = p(p -1)q) ,使得 (a + b = c) ,并且 (rad(abc) = rad(p^4(p -1)q^3) leq rad(p(p - 1)q) leq p^2q = c)

    要检查是否有无平方,则要要检查 (p^2) 直到 (√{3c})(sqrt{c}) 的平方整数

    (O(√3c))

    // Author : RioTian
    // Time : 20/12/09
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e7 + 10;
    ll _, c;
    int cnt, prime[N];
    bool st[N];
    // 线性筛筛素数
    void init(ll n) {
        for (int i = 2; i <= n; ++i) {
            if (!st[i])
                prime[++cnt] = i;
            for (int j = 1; prime[j] <= n / i; ++j) {
                st[i * prime[j]] = true;
                if (i % prime[j] == 0)
                    break;
            }
        }
    }
    void solve() {
        cin >> c;
        bool f = false;
        for (int i = 1; i <= cnt && !f; ++i) {
            ll p = prime[i];
            int s = 0;
            if (c % p == 0)
                while (c % p == 0)
                    c /= p, s++;
            if (s >= 2)
                f = true;
        }
        ll x = sqrt(c);
        if (c > 1 && 1ll * x * x == c)
            f = true;
        if (f)
            cout << "yes
    ";
        else
            cout << "no
    ";
    }
    int main() {
        freopen("in.txt", "r", stdin);
        ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
        init(N - 10);
        for (cin >> _; _; _--)
            solve();
    }
    

    D - String

    计蒜客 - T2652

    头铁记:一看到字符串就想到然往字符串方向走,但没想到是类搜索 2333

    这里贴下学长的思路(绝不是懒的打字)

    // Author : RioTian
    // Time : 20/12/09
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 10;
    const int mod = 20100403;
    ll qpow(ll a, ll b) {
        ll ans = 1;
        a %= mod;
        for (; b; a = a * a % mod, b >>= 1)
            if (b & 1)
                ans = ans * a % mod;
        return ans;
    }
    ll C(ll n, ll m) {
        ll x = 1, y = 1;
        for (int i = 1; i <= m; ++i) {
            x = x * (n - i + 1) % mod;
            y = y * i % mod;
        }
        return x * qpow(y, mod - 2) % mod;
    }
    
    int main() {
        // freopen("in.txt", "r", stdin);
        ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
        ll n, m;
        cin >> n >> m;
        cout << (C(m + n, m) - C(m + n, m - 1) % mod + mod) % mod;
    }
    

    E - Probability

    AtCoder - abc184_d

    题意:一个包里包含 X 个金币、Y 个银币、Z 个铜币。在包里钱币满足相 同颜色达到 100 之前,我们可以重复以下动作:随机选一种钱币,取出一枚, 再放入相同颜色钱币两枚。找出完成这些操作的期望值。

    根据题目的意思,其实就是每次向包里随机加入一枚钱币,直到包里某种钱币数量达到 100。本题的核心是如何计算期望。本题属于标准的动态规划求期望问题。直接套用模板即可。

    一道”简单“概率DP题,没怎么了解概率DP导致做不出

    先贴一下学长给的概率DP知识点博客:Here

    当理解基础的知识以后发现的确比较简单

    DP 数组定义

    定义 DP[i][j][k],表示有 i 枚金币, j 枚银币, k 枚铜币的期望。

    初值

    所有的期望都为零。

    递推方法

    使用逆推。

    状态转移方程:

    [s = X + Y + Z\ dp(i,j,k) = frac{i}{s}*(dp(i + 1,j,k) + 1) + frac{j}{s}*dp(i,j + 1,k) + 1) \+ frac{j}{s}*dp(i,j,k + 1) + 1)) ]

    AC代码:

    // Author : RioTian
    // Time : 20/12/09
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e2 + 10;
    double dp[N][N][N];
    int main() {
        // freopen("in.txt", "r", stdin);
        ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
        int a, b, c;
        cin >> a >> b >> c;
        for (int i = 99; i >= a; i--)
            for (int j = 99; j >= b; j--)
                for (int k = 99; k >= c; k--) {
                    // 令 t = x + y + z,减少代码量
                    double t = i + j + k;
                    dp[i][j][k] = i / t * (dp[i + 1][j][k] + 1) +
                                  j / t * (dp[i][j + 1][k] + 1) +
                                  k / t * (dp[i][j][k + 1] + 1);
                }
        // 关于 C++ 的输出控制可以在我的以前博客找到
        cout << fixed << setprecision(9) << dp[a][b][c] << endl;
    }
    

    时间和空间复杂度:

    (O(n^3))

    学习的时候发现一种模拟方法:蒙特卡洛方法模拟

    使用蒙特卡洛方法模拟

    计划等有空好好学一下这个

    本题核心其实是一个随机模拟过程,因此也可以使用蒙特卡洛方法(Monte Carlo method)来模拟这个过程。首先就不证明这个过程是收敛的,说真的,我也不大会证明,以后努力。具体的 Monte Carlo method 请看相关资料。

    因此,我们只需要模拟这样的过程,只需要足够的样本数量就可以完成模拟。我这里用了 300 次就可以满足题目的需求。为什么 300 就够了,其实就是反复测试出来的。

    AC代码

    // increment of coins
    // Using Monte Carlo method
    // Author : RioTian
    // Time : 20/12/09
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 10;
    
    double prob(int n, int a, int b, int c) {
        int u = 100 - a;
        if (u > n)
            return 0.0;
        if (n - u > (99 - b) + (99 - c))
            return 0.0;
    
        double p = 1.0, ret = 0.0;
        for (int i = 0; i < u; i++)
            p *= 1.0 * (a + i) / (a + b + c + i);
    
        for (int v = 0; v <= n - u; ++v) {
            if (v + b > 99 || n - u - v + c > 99)
                continue;
            double exp = 1.0;
            for (int j = 0; j < v; j++)
                exp *= 1.0 * (b + j) / (a + b + c + u + j);
    
            for (int j = 0; j < n - u - v; j++)
                exp *= 1.0 * (c + j) / (a + b + c + u + v + j);
    
            for (int j = v; j >= 1; j--)
                exp *= 1.0 * (u - 1 + j) / j;
    
            for (int j = n - u - v; j >= 1; j--)
                exp *= 1.0 * (u - 1 + v + j) / j;
    
            ret += (p * exp);
        }
        return ret;
    }
    
    int main() {
        // freopen("in.txt", "r", stdin);
        ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
        int a, b, c;
        cin >> a >> b >> c;
    
        double ans = 0;
        for (int i = 1; i <= 300; ++i) {
            double pa = prob(i, a, b, c);
            double pb = prob(i, b, c, a);
            double pc = prob(i, c, a, b);
            ans += i * (pa + pb + pc);
        }
    
        cout << fixed << setprecision(9) << ans << endl;
    }
    

    时间复杂度

    (O(n))。虽然也是三重循环,我们要注意到循环的次数是常数项。比如 main() 中的循环是固定 300 次,prob() 中的循环最多是 100 次。所以乘积还是常数次。

    空间复杂度

    (O(n))

  • 相关阅读:
    第04章-面向切面的Spring
    第03章-高级装配
    第02章-装配Bean
    第01章-Spring之旅
    IntelliJ IDEA打可运行jar包时的错误
    序列化+fastjson和java各种数据对象相互转化
    TinkerPop中的遍历:图的遍历策略
    TinkerPop中的遍历:图的遍历中谓词、栅栏、范围和Lambda的说明
    asp.net动态网站repeater控件使用及分页操作介绍
    HTML入门标签汇总
  • 原文地址:https://www.cnblogs.com/RioTian/p/14110922.html
Copyright © 2011-2022 走看看