CSDN 编程竞赛第十四期题解

总览

比赛链接,居然变成半周赛了…

比赛体验问题反馈如下:

  1. 自测调试会将编译与执行的 stderr 和 stdout 输出均合并后再与预期输出做比对,这导致一些编译 warning 也会影响自测,不确定是否影响实际测试。
  2. 题目描述缺乏校对,有的题目乱用符号(没有给出对应关系,需要自行脑补),有的题目缺乏数据范围,诸如此类。希望平台考虑优化题面质量,通过奖品吸引留存不是长久之计。
  3. 考试报告生成有误,导致此篇题解至今没有第一题代码。

题目分析

1. 字符串全排列

原题链接 - CSDN 每日一练 2022-06-17

题意:考虑 K 个不同字符的 K! 种全排列,现在给出其中 (K! - 1) 种方案,求剩下一种方案。 2 ≤ K ≤ 10 2 \leq K \leq 10 2K10, 只考虑非空白字符。

思路:

  • 随便写写,排序后暴力生成全排列再比对也可过

代码:C++

#include <bits/stdc++.h>
using namespace std;
std::string solution(int n, std::vector<std::string>& vec){
    if(!n)
        return "";
    std::string result = vec[0];
    sort(result.begin(), result.end());
    sort(vec.begin(), vec.end());
    int i = 0;
    while(i < n && result == vec[i]) {
        ++i;
        next_permutation(result.begin(), result.end());
    }
    return result;
}

2. 小Q新式棋盘

原题链接 - CSDN 每日一练 2022-06-17

题意:给定一个 n × n n \times n n×n 整数矩阵,求有多少对行、列满足行的元素和小于列的元素和。 1 ≤ n ≤ 100 1 \leq n \leq 100 1n100, 元素 ∈ [ 1 , 100 ] \in [1, 100] [1,100]

思路:

  • 求出所有行、列元素和,排序后扫一遍统计即可。

代码:C++

#include <bits/stdc++.h>
using namespace std;
int solution(int n, std::vector<std::vector<std::string>>& vec){
    vector<int> row(n), col(n);
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j) {
            int v = stoi(vec[i][j]);
            row[i] += v;
            col[j] += v;
        }
    sort(row.begin(), row.end());
    sort(col.begin(), col.end());
    int ans = 0;
    for(int i = 0, j = 0; i < n; ++i) {
        for( ; j < n && row[j] < col[i]; ++j);
        ans += j;
    }
    return ans;
}

3. 因数-数字游戏

原题链接 - CSDN 每日一练 2022-06-17

题意:两人玩一个公平游戏,初始有一个数字 n n n,两人轮流修改它,每次可以将 n n n 替换成满足 1 < x < n , x ∣ n 1 < x < n, x | n 1<x<n,xn x x x,不能操作者胜,问先手是否必胜(是输出 1,否输出 2)。 1 ≤ n ≤ 1 0 13 1 \leq n \leq 10^{13} 1n1013

思路:

  • 考虑到 n n n 的因子数不多( n ≤ 1 0 13 n \leq 10^{13} n1013 σ ( n ) ≤ 10752 \sigma(n) \leq 10752 σ(n)10752),现场写了个 O ( σ 2 ( n ) ) \mathcal{O}(\sigma^2(n)) O(σ2(n)) 暴力 DP,把所有 n n n 的约数拿出来后暴力求 SG 值,发现复杂度还行。
  • 实际上可以多分析一步必败条件,先手必胜当且仅当 Ω ( n ) ≠ 2 \Omega(n) \neq 2 Ω(n)=2 Ω ( n ) \Omega(n) Ω(n) n n n 的可重质因子个数),这也导致上述 DP 可以剪枝,从而跑得很快。

代码:C++

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int solution(LL n){
    vector<LL> fct = {1};
    for(LL i = 2; i <= n; ++i) {
        if(i * i > n)
            i = n;
        int c = 0;
        for( ; n % i == 0; n /= i, ++c);
        if(!c)
            continue;
        vector<LL> nxt;
        for(LL u: fct) {
            for(int j = 0; j <= c; ++j) {
                nxt.push_back(u);
                u *= i;
            }
        }
        fct = move(nxt);
    }
    sort(fct.begin(), fct.end());
    int m = fct.size();
    vector<bool> dp(m);
    for(int i = 0; i < m; ++i) {
        bool upd = 0;
        for(int j = 1; !dp[i] && j < i; ++j)
            if(fct[i] % fct[j] == 0) {
                dp[i] = !dp[j];
                upd = 1;
            }
        if(!upd)
            dp[i] = 1;
        // printf("fct %lld: %d\n", fct[i], (int)dp[i]);
    }
    return dp[m - 1] ? 1 : 2;
}

4. 编码

原题链接 - CSDN 每日一练 2022-06-17
原题链接 - 洛谷 P1246

题意:考虑字符集为小写字母、长度不超过 6、不包含重复字符、内部字符按升序排列的所有字符串,把它们按照长度升序排列,相同长度继续按字典序升序排列,得到一个字符串列表 L L L,现在给定一个字符串 S S S,问 S S S 是否在 L L L 中,如果不在则输出 0,否则输出 S S S 的是第几个元素。别问数据范围,问就是没有。

思路:

  • 现场写的时候没有限制长度不超过 6 也可以通过,大概是数据里没有长度超过 6 还满足小写字母升序的字符串。
  • 简单判断下不存在的情况,剩下是一个数位 DP 问题,即统计有多少字符串 T T T 比给定字符串 S S S 小。
    • 对于 ∣ S ∣ > ∣ T ∣ |S| > |T| S>T 的情况,直接组合数统计;对于 ∣ S ∣ = ∣ T ∣ |S| = |T| S=T 的情况,枚举第一个 S [ i ] > T [ i ] S[i] > T[i] S[i]>T[i] 的位置,枚举 T [ i ] T[i] T[i],剩下还是组合数统计。
  • 时间复杂度 O ( ∣ S ∣ ∣ Σ ∣ ) \mathcal{O}(|S| |\Sigma|) O(S∣∣Σ∣)。实际上枚举 T [ i ] T[i] T[i] 后的组合数可以合并计算,从而做到 O ( ∣ S ∣ + ∣ Σ ∣ ) \mathcal{O}(|S| + |\Sigma|) O(S+∣Σ∣),但没必要。

代码:C++

#include <bits/stdc++.h>
using namespace std;
int solution(std::string word){
    const int maxd = 26;
    int n = word.size();
    if(!n || n > maxd || !(word[0] >= 'a' && word[0] <= 'z'))
        return 0;
    word[0] -= 'a';
    for(int i = 1; i < n; ++i) {
        if(!(word[i] >= 'a' && word[i] <= 'z'))
            return 0;
        word[i] -= 'a';
        if(word[i] <= word[i - 1])
            return 0;
    }
    typedef long long LL;
    LL ans = 1;
    vector<vector<LL> > bin(maxd + 1, vector<LL>(maxd + 1));
    for(int i = 0; i <= maxd; ++i) {
        bin[i][0] = bin[i][i] = 1;
        for(int j = 1; j < i; ++j)
            bin[i][j] = bin[i - 1][j - 1] + bin[i - 1][j];
    }
    for(int i = 0; i < n; ++i)
        for(int j = i ? word[i - 1] + 1 : 0; j < word[i]; ++j)
            ans += bin[maxd - 1 - j][n - 1 - i];
    for(int i = 1; i < n; ++i)
        ans += bin[maxd][i];
    return ans;
}

后续

别看了哪有下期啊。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值