2021 年东北林业大学 ACM 校赛ph的题解

280 阅读2分钟

2021 年东北林业大学 ACM 校赛题解

A - 前进东北林业大学

#include <stdio.h>
int main(){puts("203");}

B - 病毒传播

假如我们每天传播 kk 人,一共 nn 个患者,那么每天增加的人数就是 n×kn\times k

#include <iostream>

using namespace std;

using ULL = unsigned long long;

int main() {
    int n, x;
    cin >> x >> n;
    ULL now = 1;
    for (int i = 0; i < n; i++) {
        now += now * x;
    }
    cout << now << '\n';
}

C - 语汐和她的加权成绩

#include <iostream>
#include <set>
#include <vector>
using namespace std;

multiset<double, greater<double>> s;

int main() {
    int n;
    cin >> n;

    while (n--) {
        int m;
        cin >> m;
        double sum = 0;
        double all = 0;
        for (int i = 0; i < m; i++) {
            double a, b;
            cin >> a >> b;
            sum += a * b;
            all += b;
        }
        s.insert(sum / all);
    }

    for (auto x : s) {
        printf("%.2f\n", x);
    }

    return 0;
}

D - jwgg的字符串

找到一个替换即可。

#include <iostream>

using namespace std;

int main() {
    string a, b, c;
    cin >> a >> b >> c;
    if (a.find(b) == a.npos) {
        cout << a << c << '\n';
    } else {
        int kki = a.find(b);
        cout << a.substr(0 , kki) 
             << c 
             << a.substr(kki +
                         b.length()) 
             << '\n';
    }
    return 0;
}

E - 小A的二进制

了解一下二进制运算符,即可。

#include <iostream>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        long long a, b;
        cin >> a >> b;
        int ans = 0;
        for (int i = 0; i < 100; i++) {
            ans += (a & 1) != (b & 1);
            a >>= 1, b >>= 1;
        }
        cout << ans << '\n';
    }
    return 0;
}

或者

#include <bitset>
#include <iostream>
using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        unsigned long long a, b;
        cin >> a >> b;
        bitset<64> s(a ^ b);
        cout << s.count() << '\n';
    }
}

F - 小H与小Y

很难的一道题

根本做不上来

G - 飞,比跑快吧

最好的例子

我们直接看三号样例吧。

我们每次飞行都有若干次降落和上升组成,并且这些动作是紧紧挨着的。

那么也就可以换成一次下降+一次上升,或者一次上升和一次下降。

那么选择哪个呢?

如果先下降后上升,难免会产生后效性,也就是说,我们的最低点有可能会被上升所干扰。

因此我们选择后者,那么我们每次将未落地的部分先处理完再处理最后一部分即可。

#include <cmath>
#include <iostream>

using namespace std;

int main() {
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    int t;
    cin >> t;

    while (t--) {
        int a, b, x, y;
        // cin >> a >> b >> x >> y;
        scanf("%d%d%d%d", &a, &b, &x, &y);
        int times = y / 8;

        double havx = times * 9.0 * b / a;
        bool ans = false;
        
        if (havx >= x)
            ans = true;
        else if (times * 8 == y)
            ;
        else {
            havx = x - havx;
            double havy = y - times * 8;
            havy += 1;
            if (b * havy >= a * havx || 
                fabs(b * havy - a * havx) <= 1e-8)
                ans = true;
        }
        
        if (ans)
            puts("yes");
        else
            puts("no");
    }

    return 0;
}

H - 油豆腐和八重子。

想法还么有完全证明出来,看看出题人怎么证明的。

看代码自然就懂啦~~。

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

int main() {
    int n, k;
    cin >> n >> k;
    using LL = long long;

    vector<LL> A(2 * n + 1);

    for (int i = 1; i <= n; i++) cin >> A[i];
    for (int i = n + 1; i <= 2 * n; i++) A[i] = A[i - n];

    if (k == 1) {
        cout << A[1] << '\n';
        return 0;
    }

    LL ans = -1;
    LL sum = A[1];
    for (int i = 2; i <= 2 * n; i++) {
        sum += A[i];
        if (i <= k) {
            LL tmp = k - i;
            if (tmp & 1) {
                tmp = tmp / 2 * A[i] + 
                     (tmp / 2 + 1) * A[i - 1];
            } else {
                tmp = tmp / 2 * (A[i] + A[i - 1]);
            }

            ans = max(ans, sum + tmp);
        }
    }

    A[0] = A[1];
    reverse(A.begin() + 1, A.end());

    for (int i = 2 * n; i > 0; i--) A[i] = A[i - 1];

    sum = A[1];
    for (int i = 2; i <= 2 * n; i++) {
        sum += A[i];
        if (i <= k) {
            LL tmp = k - i;
            if (tmp & 1) {
                tmp = tmp / 2 * A[i] + 
                     (tmp / 2 + 1) * A[i - 1];
            } else {
                tmp = tmp / 2 * (A[i] + A[i - 1]);
            }

            ans = max(ans, sum + tmp);
        }
    }

    cout << ans << '\n';
}

I - 你什么时候来啊,我的甘雨!

我们从一到十的二进制表示如下

/*
 * 0 :
 * 1 : 1
 * 2 : 10
 * 3 : 11
 * 4 : 100
 * 5 : 101
 * 6 : 110
 * 7 : 111
 * 8 : 1000
 * 9 : 1001
 */

显然高位需要尽可能的大,低位需要尽可能的宽,尽可能的小。那么因此高位填 9 ,低位填 8

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int main() {
    int t;
    cin >> t;

    while (t--) {
        int n;
        cin >> n;

        for (int i = n; i >= 1; i--) 
            if ((i - 1) * 4 + 1 <= n)  putchar('8');
            else putchar('9');
        
        puts("");
    }
}

J - 刻晴和她的烤吃虎鱼

我们只要遵循生鱼先烤的原则即可保证我们的答案符合最优,然后暴力即可。

#include <iostream>

using namespace std;

int main() {
    int t;
    cin >> t;

    while (t--) {
        long long n, m, k;
        cin >> n >> m >> k;

        long long fresh, cooked;
        fresh = cooked = 0;

        long long ans;
        long long lasted;
        for (ans = 60;; ans += 15) {
            lasted = min(n, fresh);
            fresh -= lasted;
            cooked += lasted;
            if (n - lasted) {
                lasted = min(n - lasted, cooked - lasted);
                cooked -= lasted;
            }
            if (ans % 60 == 0) {
                lasted = min(k, m * 2);
                k -= lasted;
                fresh += lasted;
            }
            if (k == 0 && fresh == 0 && cooked == 0) break;
        }
        cout << ans << '\n';
    }

    return 0;
}

K - 数数

我们不难发现这个只是一个插空法。全部排上 1 然后插入 0 即可。

  1. n=2×kn = 2 \times kF(n)=Ck+1k+Ck+2k1+...+C2×k+10F(n)=C_{k+1}^k+C_{k+2}^{k-1}+...+C_{2\times k + 1}^{0}
  2. n=2×k+1n = 2 \times k + 1F(n)=Ck+1k+1+Ck+2k+...+C2×k+20F(n)=C_{k+1}^{k+1}+C_{k+2}^{k}+...+C_{2\times k + 2}^0

由于组合数的特征,不难发现有 F(n)=F(n1)+F(n2)F(n)=F(n-1)+F(n-2)

那么也就变为斐波那契数列了,

我们再看看首项为2,第二项为 3。

#include <iostream>

using namespace std;

const int N = 1e5 + 10;
const int MOD = 19260817;

long long Fib[N];

int main() {
    int t;
    cin >> t;

    Fib[0] = 1;
    Fib[1] = 2;
    for (int i = 2; i < N; i++) Fib[i] = (Fib[i - 1] + Fib[i - 2]) % MOD;

    while (t--) {
        int n;
        scanf("%d", &n);
        res = Fib[n];
        printf("%lld\n", res);
    }
    return 0;
}

L - 语汐与小哥哥

我们采用贪心的原则,首先按从小到大的顺序排序,然后从大到小填数,使他们每个人的总分尽可能的最小。然后,对于每个人我们也按照贪心的原则,让他加上最大的值,如果比之前的最小总分的最大值大的话,那么这个人就有可能成为TA的对象。

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

int main() {
    int n;
    cin >> n;
    vector<long long> v(n);

    for (int i = 0; i < n; i++) scanf("%lld", &v[i]);
    sort(begin(v), end(v));

    long long maxx = -1;

    for (int i = 0; i < n; i++) {
        maxx = max(maxx, v[i] + n - i);
    }
    int res = 0;
    for (int i = 0; i < n; i++) {
        if (v[i] + n >= maxx) res++;
    }
    cout << res << '\n';

    return 0;
}