2024年广州大学程序设计竞赛新生赛(同步赛)题解

比赛链接

A. 在ACM里打ACM

Solution

模拟题意即可,需要记录的信息是:总的罚时 p e n pen pen,总共 a c ac ac 的题目数量 a c c acc acc,对 13 13 13 个题都记录两个数,一个是 f a i l fail fail 表示非 a c ac ac 提交的额外罚时,一个是 i s A c isAc isAc 表示这题是否 a c ac ac,因为可能出现过了某个题还继续交的情况,这种情况应该忽略任何这道题的提交。

C++ Code

#include <bits/stdc++.h>

using i64 = int64_t;
using u64 = uint64_t;
using f64 = double_t;
using i128 = __int128_t;

struct Team {
   
    std::string s;
    int acc = 0;
    int pen = 0;
    std::array<int, 13> fail{
   };
    std::array<int, 13> isAc{
   };
};

int main() {
   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    std::cout << std::fixed << std::setprecision(12);
    
    int n;
    std::cin >> n;

    std::vector<Team> team;
    std::map<std::string, int> mp;

    int m = 0;
    for (int i = 0; i < n; i++) {
   
        std::string s;
        char p;
        int ac;
        int t;
        std::cin >> s >> p >> ac >> t;
        if (!mp.contains(s)) {
   
            mp[s] = m++;
            team.push_back({
   s});
        }
        p -= 'A';
        int id = mp[s];
        if (ac == 0) {
   
            if (!team[id].isAc[p]) {
   
                team[id].fail[p] += 20;
            }
            continue;
        }
        if (!team[id].isAc[p]) {
   
            team[id].acc++;
            team[id].isAc[p] = 1;
            team[id].pen += t + team[id].fail[p];
        }
    }

    std::ranges::sort(team, [&](const auto &x, const auto &y) {
   
        if (x.acc != y.acc) {
   
            return x.acc > y.acc;
        }
        return x.pen < y.pen;
    });

    std::cout << team[0].s << " " << team[0].acc << " " << team[0].pen << "\n";
    
    return 0;
}

B. 逆=辶+屰

题目大意

给定一个数字 n n n q q q 个询问,每个询问给出两个正整数 k ,   r k,\ r k, r,要求回答:

  • 是否存在 k k k不同正整数 c 1 ,   c 2 ,   ⋯   ,   c k c_1, \ c_2, \ \cdots, \ c_k c1, c2, , ck满足 n ≡ r ( m o d c i ) ,   i ∈ [ 1 , k ] n \equiv r \pmod{c_i}, \ i \in [1, k] nr(modci), i[1,k]

数据范围

  • 1 ⩽ n ⩽ 1 0 6 1 \leqslant n \leqslant 10^6 1n106
  • 1 ⩽ q ⩽ 1 0 4 1 \leqslant q \leqslant 10^4 1q104;
  • 1 ⩽ k ⩽ 1 0 6 1 \leqslant k \leqslant 10^6 1k106
  • 0 ⩽ r < n 0 \leqslant r < n 0r<n

Solution1

由同余的性质,得到 ( n − r ) ≡ 0 ( m o d c i ) ,   i ∈ [ 1 , k ] (n - r) \equiv 0 \pmod{c_i}, \ i \in [1, k] (nr)0(modci), i[1,k],也就是说 ∀ c i \forall c_i ci 都是 n − r n - r nr 的因数。

所以我们只要对 n − r n - r nr 进行质因数分解,然后计算所有 > r > r >r 的因数有多少即可。

要保证 > r > r >r 是因为, r r r 是模 c i c_i ci 后的结果,说明 0 ⩽ r < c i 0 \leqslant r < c_i 0r<ci

时间复杂度 O ( q n − r ) \mathcal{O}(q\sqrt{n - r}) O(qnr )

  • 大约是 1 0 4 × 1 0 6 = 1 0 7 10^4 \times \sqrt{10^6} = 10^7 104×106 =107,完全可以过。

C++ Code for Solution1

#include <bits/stdc++.h>

using i64 = long long;

int calc(int x, int r) {
   
    int ans = 0;
    for (int i = 1; i * i <= x; i++) {
   
        if (x % i == 0) {
   
            ans += i > r;
            if (i * i != x) {
   
                ans += x / i > r;
            }
        }
    }
    return ans;
}

int main() {
   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, q;
    std::cin >> n >> q;

    while (q--) {
   
        int k, r;
        std::cin >> k >> r;

        int cnt = calc(n - r, r);
        if (cnt >= k) {
   
            std::cout << "YES\n";
        } else {
   
            std::cout << "NO\n";
        }
    }
    
    return 0;
}

Solution2

考虑到 0 ⩽ r < n 0 \leqslant r < n 0r<n,所以 n ≡ r ( m o d c i ) n \equiv r \pmod{c_i} nr(modci) c i c_i ci 一定不会超过 n n n,否则就有 n ≡ n ( m o d N ) n \equiv n \pmod{N} nn(modN),其中 N > n N > n N>n

因此我们可以直接从 1 1 1 n n n 枚举可能的模数 c i c_i ci,用一个 c n t cnt cnt 数组记录,例如 c n t [ i ] cnt[i] cnt[i] 表示有多少 c c c 满足 n ≡ i ( m o d c ) n \equiv i \pmod{c} ni(modc)

最后判断的时候只要看是否有 c n t [ r ] ⩾ k cnt[r] \geqslant k cnt[r]k 即可。

时间复杂度 O ( n + q ) \mathcal{O}(n + q) O(n+q)

C++ Code for Solution2

#include <bits/stdc++.h>

using i64 = long long;

int main() {
   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, q;
    std::cin >> n >> q;

    std::vector<int> cnt(n);
    for (int i = 1; i <= n; i++) {
   
        cnt[n % i]++;
    }

    while (q--) {
   
        int k, r;
        std::cin >> k >> r;

        if (cnt[r] >= k) {
   
            std::cout << "YES\n";
        } else {
   
            std::cout << "NO\n";
        }
    }
    
    return 0;
}

C. 老树也有几颗新芽

题目大意

M o r t i s Mortis Mortis 在穿过小巷时,遇到了一种奇特的硬币 G G G。一开始有 n n n G G G,分别记为 G i ,   i ∈ [ 1 ,   n ] G_i, \ i \in [1, \ n] Gi, i[1, n],对应的本地计数器分别为 c n t i ,   i ∈ [ 1 ,   n ] cnt_i, \ i \in [1, \ n] cnti, i[1, n]。一开始 ∀ c n t i = 0 \forall cnt_i = 0 cnti=0

现在你需要捡走地上所有的硬币。每一次操作你可以任选一枚硬币 G x G_x Gx 捡走,捡走后会按顺序发生以下事件:
① 随机选择一枚仍未被检走 G y G_y Gy,使被选择的 G y G_y Gy 的本地计数器 c n t y : = c n t y + 1 cnt_y := cnt_y + 1 cnty:=cnty+1
② 再在地上生成 c n t x cnt_x cntx新的硬币 G G G,均初始化 c n t = 0 cnt = 0 cnt=0

已知 M o r t i s Mortis Mortis 在每次操作时可以知道所有硬币的计数器 c n t i cnt_i cnti,因此总会采取最优策略。

求在最坏情况下,至少需要多少次操作才能将所有的硬币捡走。

Solution

最坏原则:在最坏情况下, c n t i : = c n t i + 1 cnt_i := cnt_i + 1 cnti:=cnti+1 操作会尽可能落到 c n t i cnt_i cnti 较小的 G i G_i Gi 上,我们也应优先捡起 c n t i cnt_i cnti 较小的 G i G_i Gi。因此对于 n n n ∀ c n t i = 0 \forall cnt_i = 0 cnti=0 的硬币,若干次操作后总是会得到剩下一枚硬币 G G G c n t = n − 1 cnt = n - 1 cnt=n1,然后捡起这枚硬币,将问题转化为捡起 n − 1 n - 1 n1 ∀ c n t i = 0 \forall cnt_i = 0 cnti=0 的硬币。

我们用 c d p [ i ] cdp[i] cdp[i] 表示将 i i i ∀ c n t j = 0 \forall cnt_j = 0 cntj=0 的硬币全部捡起的最小操作次数, c d p [ 0 ] = 0 cdp[0] = 0 cdp[0]=0

d p [ i ] = c d p [ i ] − c d p [ i − 1 ] dp[i] = cdp[i] - cdp[i - 1] dp[i]=cdp[i]cdp[i1],这样 d p [ i ] dp[i] dp[i] 的定义就是从 i i i ∀ c n t j = 0 ,   j ∈ [ 1 ,   i ] \forall cnt_j = 0, \ j \in [1, \ i] cntj=0, j[1, i] 的硬币变为 i − 1 i - 1 i1 ∀ c n t j = 0 ,   j ∈ [ 1 ,   i − 1 ] \forall cnt_j = 0, \ j \in [1, \ i - 1] cntj=0, j[1, i1] 的硬币的最小操作次数。

下面求解 d p [ i ] dp[i] dp[i]

我们可以根据最坏原则手玩一下前 8 8 8 d p dp dp 值,发现会得到 1 ,   2 ,   3 ,   5 ,   6 ,   10 ,   11 ,   16 1, \ 2, \ 3, \ 5, \ 6, \ 10, \ 11, \ 16 1, 2, 3, 5, 6, 10, 11, 16

例如 d p [ 5 ] = 6 dp[5] = 6 dp[5]=6 的模拟如下( [ k ] [k] [k] 表示计数器的值为 k k k):

[ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 1 ] [ 0 ] [ 1 ] [ 1 ] [ 1 ] [ 2 ] [ 0 ] [ 3 ] [ 4 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [0][0][0][0][0] \\ [0][0][0][1] \\ [0][1][1] \\ [1][2] \\ [0][3] \\ [4] \\ [0][0][0][0] [0][0][0][0][0][0][0][0][1][0][1][1][1][2][0][3][4][0][0][0][0]

再有 d p [ 7 ] = 11 dp[7] = 11 dp[7]=11 的模拟如下:

[ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 1 ] [ 0 ] [ 0 ] [ 0 ] [ 1 ] [ 1 ] [ 0 ] [ 1 ] [ 1 ] [ 1 ] [ 1 ] [ 1 ] [ 2 ] [ 0 ] [ 2 ] [ 2 ] [ 2 ] [ 3 ] [ 0 ] [ 0 ] [ 4 ] [ 1 ] [ 4 ] [ 0 ] [ 5 ] [ 6 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] [0][0][0][0][0][0][0] \\ [0][0][0][0][0][1] \\ [0][0][0][1][1] \\ [0][1][1][1] \\ [1][1][2] \\ [0][2][2] \\ [2][3] \\ [0][0][4] \\ [1][4] \\ [0][5] \\ [6] \\ [0][0][0][0][0][0] [0][0][0][0][0][0][0][0][0][0][0][0][1][0][0][0][1][1][0][1][1][1][1][1][2][0][2][2][2][3][0][0][4][1][4][0][5][6][0][0][0][0][0][0]

对于这个

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值