CSDN 编程竞赛第五期题解

总览

退役竞赛选手出来混个存在感,比赛链接,似乎 CSDN 要做成周赛了。

题目难度上比较简单,部分是原题,需要注意些细节。

赛途不允许切出屏幕和使用通讯软件,整体比较像笔试,偏向于考察应试技能,那么有原题也说得过去。

赛前出现长时间无法进入的情况,赛中编辑器和自助评测使用体验较差,例如代码模板对用户不友好(函数参数不一致、头文件配置不灵活等)、语法补全提示相关度忽高忽低、评测结果没有剔除 stderr 等。同天还有力扣周赛和百度之星初赛,它们所在平台在编辑和评测上的用户体验就好些。

题目分析

1. 寻因找祖

题意:求约数个数为 n n n 的最小正整数 x x x n ≤ 1000 n \leq 1000 n1000

思路:(比较古老的原题,加强版见 SGU 473

  • x = ∏ i = 1 t p i e i x = \prod_{i = 1}^{t}{{p_i}^{e_i}} x=i=1tpiei ( p i < p i + 1 , p i  is prime ) (p_i < p_{i + 1}, p_i\text{ is prime}) (pi<pi+1,pi is prime),则它的约数个数 σ ( x ) = ∏ i = 1 t ( e i + 1 ) \sigma(x) = \prod_{i = 1}^{t}{(e_i + 1)} σ(x)=i=1t(ei+1),因此 e i e_i ei 的范围被限制在 ( e i + 1 ) ∣ n (e_i + 1) | n (ei+1)n
  • 如果两个数字的 { e i } \{e_i\} {ei} 集合一样,那么它们的约数个数相同。
    • 对于一个固定的 { e i } \{e_i\} {ei} 集合,考虑其对应的数字 x x x,如果存在 e i < e i + 1 e_i < e_{i + 1} ei<ei+1,那么可以构造出一个更小的数字 x ′ = ∏ i = 1 t p i e ′ i x' = \prod_{i = 1}^{t}{{p_i}^{{e'}_i}} x=i=1tpiei 对应该集合,这里 e ′ i = e i + 1 {e'}_i = e_{i + 1} ei=ei+1, e ′ i + 1 = e i {e'}_{i + 1} = e_i ei+1=ei
    • 对于一个固定的 { e i } \{e_i\} {ei} 集合,最小的正整数 x x x 一定只用到最小的 t t t 个质数,且 e i ≥ e i + 1 e_i \geq e_{i + 1} eiei+1,否则总能通过调整得到更小的正整数 x x x
  • 接下来可以暴力搜索(可以手动验证状态不多)或者 DP,但考虑到 n  is prime n\text{ is prime} n is prime 时答案可能非常大(例如 2 996 2^{996} 2996),不如直接暴力搜索。

代码:Python3

class Solution:
    def __init__(self) -> None:
        self.pr = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
        self.ans = 1

    def dfs(self, dep, cur, rem, upp):
        if rem == 1:
            self.ans = min(self.ans, cur)
            return
        if dep >= len(self.pr):
            return
        for i in range(1, upp + 1):
            if i * i > rem:
                break
            if rem % i > 0:
                continue
            if i > 1:
                self.dfs(dep + 1, cur * (self.pr[dep] ** (i - 1)), rem // i, i)
            j = rem // i
            if i < j:  # j > 1
                self.dfs(dep + 1, cur * (self.pr[dep] ** (j - 1)), rem // j, j)

    def solution(self, n):
        self.ans = 10 ** 1000
        self.dfs(0, 1, n, n)
        return self.ans

2. 通货膨胀-x国货币

题意:构造一个最长的正整数序列 A = [ a 1 , a 2 , … , a m ] A = [a_1, a_2, \ldots, a_m] A=[a1,a2,,am],使得 a 1 = n a_1 = n a1=n, a i + 1 ∣ a i a_{i + 1} | a_i ai+1ai,输出 m m m 的最大值。数据范围好像是 n ≤ 1 0 9 n \leq 10^9 n109

思路:(比较古老的原题)

  • 贪心构造即可,使每个 a i a i + 1 \frac{a_i}{a_{i + 1}} ai+1ai 为质数不会使解更差。令 n = ∏ i = 1 t p i e i n = \prod_{i = 1}^{t}{{p_i}^{e_i}} n=i=1tpiei ( p i < p i + 1 , p i  is prime ) (p_i < p_{i + 1}, p_i\text{ is prime}) (pi<pi+1,pi is prime),则答案为 1 + ∑ i = 1 t e i 1 + \sum_{i = 1}^{t}{e_i} 1+i=1tei

代码:C++

int solution(int n) {
    typedef long long LL;
    int result = 1;
    for(int i = 2; i <= n; ++i) {
        if((LL)i * i > n)
            i = n;
        for( ; n % i == 0; n /= i, ++result);
    }
    return result;
}

3. 莫名其妙的键盘

题意:初始有一个空串 S S S,每次操作可以向其末尾增加一个小写字母,如果增加的字符是元音字符,则在增加后需要将 S S S 反转,然后才视为此次操作结束。给定目标串 T T T,问有多少种操作方案能使 S S S 在操作结束后变成 T T T。数据范围好像是 ∣ T ∣ ≤ 100 |T| \leq 100 T100

思路:

  • S S S 在任意时刻必须是 T T T T T T 的反转串的一个区间,那么记录 S S S 变成每个区间的方案即可 DP 出最终答案,时间复杂度 O ( ∣ T ∣ 2 ) \mathcal{O}(|T|^2) O(T2)

代码:C++

#include <bits/stdc++.h>
using namespace std;
int solution(std::string str){
    int n = str.size();
    vector<bool> sp(n);
    vector<vector<pair<int, int> > > dp(n, vector<pair<int, int> >(n));
    for(int i = 0; i < n; ++i) {
        dp[i][i] = {1, 1};
        char ch = str[i];
        sp[i] = ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u';
    }
    for(int len = 2; len <= n; ++len)
        for(int L = 0, R = len - 1; R < n; ++L, ++R) {
            if(sp[L]) {
                dp[L][R].first += dp[L + 1][R].second;
            } else {
                dp[L][R].second += dp[L + 1][R].second;
            }
            if(sp[R]) {
                dp[L][R].second += dp[L][R - 1].first;
            } else {
                dp[L][R].first += dp[L][R - 1].first;
            }
        }
    return dp[0][n - 1].first;
}

4. 三而竭

题意:给定正整数 n n n k k k,求最小的正整数 x x x 使得 ∑ i ≥ 0 ⌊ x k i ⌋ ≥ n \sum_{i \geq 0}{\left\lfloor\frac{x}{k^i}\right\rfloor} \geq n i0kixn。数据范围好像是 n ≤ 1 0 9 n \leq 10^9 n109, 2 ≤ k ≤ 10 2 \leq k \leq 10 2k10

思路:(比较古老的原题)

  • 函数 f ( x ) = ∑ i ≥ 0 ⌊ x k i ⌋ f(x) = \sum_{i \geq 0}{\left\lfloor\frac{x}{k^i}\right\rfloor} f(x)=i0kix 单调递增,且 f ( n ) ≥ n f(n) \geq n f(n)n,二分即可,时间复杂度 O ( log ⁡ k n log ⁡ 2 n ) \mathcal{O}(\log_k{n} \log_2{n}) O(logknlog2n)
  • 也可以做到 O ( log ⁡ k n ) \mathcal{O}(\log_k{n}) O(logkn),留给读者自行探索。感兴趣可以来做下我出的题目 「LibreOJ β Round #5」最小倍数,提交的代码是公开的。

代码:C++

int solution(std::vector<int>& vec){
    int n = vec[0], k = vec[1];
    int L = 1, R = n;
    while(L < R) {
        int M = (L + R) >> 1;
        int ctr = 0, rem = M;
        for( ; ctr < n && rem > 0; ctr += rem, rem /= k);
        if(ctr < n) {
            L = M + 1;
        } else {
            R = M;
        }
    }
    return L;
}

后续

别看了哪有下期啊。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值