• CSDN 编程竞赛第七期题解


    总览

    被多次通知了还是出来营业下(😛),比赛链接

    就体验而言:反作弊系统比一般笔试系统还严格些,评测系统比一般比赛系统还粗糙些,题库比较老旧且缺乏校对。

    • 有很直观的经典题,但感觉是在考察经验积累和背诵能力,偏应试;
    • 存在题目描述有歧义,存在数据范围模糊或与代码模板的数据类型不匹配;
    • 评测机性能难以评估,导致难以通过数据范围对可行算法复杂度进行合理判断;
    • 反作弊系统致使网页编辑器无法充分使用复制、剪切功能,调整、复制重复代码过多时只能手动重写;
    • 写这篇文章时概率性打不开自己的考试报告(差点本文就没有代码了 😛)。

    第六期的问题暂且略过,至少 bug 们没有都留到下一期。

    题目分析

    1. 奇偶排序

    题意:把一个数组 v v v 重排,使所有奇数在所有偶数的左边。数据范围似乎没说(当作 ∣ v ∣ ≤ 1 0 5 |v| \leq 10^5 v105 好了),也没保证至少有一个奇数、一个偶数(😛)。

    思路:

    • 考虑罚时随便写写,只要是线性复杂度就不容易被卡。
    • 有闲工夫的同学可以维护未处理的区间,每次看区间左端点元素是否要放到区间末尾,然后把此元素视为处理。

    代码:C++

    std::vector<int> solution(int n, std::vector<int>& vec){
        std::vector<int> result, even;
        for(int x: vec)
            if(x & 1) {
                result.push_back(x);
            } else {
                even.push_back(x);
            }
        result.insert(result.end(), even.begin(), even.end());
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2. 小艺照镜子

    题意:给一个字符串 s s s,求它的最长回文子串长度。数据范围 ∣ s ∣ ≤ 1 0 4 |s| \leq 10^4 s104,没保证字符串由非空白的可见字符构成(但代码模版只能处理非空白字符的情况 😛)

    思路:

    • 假设输入只有字母、数字,现场回忆 manacher 做了什么,凑凑系数对上就行 😛
    • 找一个不包含在输入内的字符填充到 s s s 的相邻字母间,得到新串 t t t,则 s s s 长度奇数、偶数的回文子串均变成 t t t 长度奇数的回文子串
    • 从左到右计算以每个位置 i i i 为中心的最长奇数回文子串的端点到中心距离 h [ i ] h[i] h[i];在计算其值时,通过维护已经计算过的最右奇数回文子串给 h [ i ] h[i] h[i] 赋一个合理的初值使得 i + h [ i ] i + h[i] i+h[i] 总能不减即可做到线性

    代码:C++

    int solution(std::string s){
        int n = s.size(), m = n + n + 1;
        std::string t = "#";
        for(int i = 0; i < n; ++i) {
            t.push_back(s[i]);
            t.push_back('#');
        }
        int result = 0;
        std::vector<int> h(m);
        for(int i = 0, C = 0, R = 0; i < m; ++i) {
            h[i] = i < R ? std::min(h[C + C - i], R - i) : 0;
            for( ; i - h[i] >= 0 && i + h[i] < m && t[i - h[i]] == t[i + h[i]]; ++h[i]);
            if(i + h[i] > R) {
                C = i;
                R = i + h[i];
            }
            // printf("%c %d\n", t[i], h[i]);
            result = std::max(result, h[i] - 1);
        }
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3. 交换后的or

    题意:给两个长度为 n n n 的二进制数 A A A B B B,选两个不同二进制位分别将 A A A B B B 上对应值交换,得到 A ′ A' A B ′ B' B,问有多少种方式可以使得 ( A ∣ B ) ≠ ( A ′ ∣ B ′ ) (A | B) \neq (A' | B') (AB)=(AB),这里 ∣ | 代表按位或。数据范围 n ≤ 1 0 5 n \leq 10^5 n105 但没有保证答案可以用有符号 32 位整型数表示(😛)

    思路:

    • 答案只和被交换的两个二进制位 i i i j j j 上分别对应 A A A B B B 的数位是什么有关
    • 对于一个二进制位, A A A B B B 的数位最多有 4 种可能,当 i i i j j j 对应同一种可能时一定对答案没有贡献
    • 枚举这 ( 4 2 ) {4 \choose 2} (24) 种交换的可能,计算对答案的贡献即可

    代码:C++

    typedef long long LL;
    LL solution(int n, std::string str1, std::string str2){
        std::vector<int> ctr(4);
        for(int i = 0; i < n; ++i) {
            int msk = (str1[i] == '1') << 1 | (str2[i] == '1');
            ++ctr[msk];
        }
        LL result = 0;
        for(int i = 0; i < 4; ++i)
            for(int j = i + 1; j < 4; ++j) {
                if((i >> 1) == (j >> 1))
                    continue;
                int d0 = ((i >> 1) | (i & 1)) ^ ((j >> 1) | (i & 1));
                int d1 = ((j >> 1) | (j & 1)) ^ ((i >> 1) | (j & 1));
                if(d0 || d1)
                    result += (LL)ctr[i] * ctr[j];
            }
        return result;
    }
    int main() {
        int n;
        std::string str1;
        std::string str2;
        std::cin>>n;
        std::cin>>str1;
        std::cin>>str2;
        LL result = solution(n, str1, str2);
        std::cout<<result<<std::endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    4. 去除整数

    题意:给定整数 n n n, m m m, a 1 , a 2 , … , a m a_1, a_2, \ldots, a_m a1,a2,,am,统计满足 1 ≤ x ≤ n 1 \leq x \leq n 1xn, ∀ i = 1 , 2 , … , m a i ∤ x \forall_{i = 1, 2, \ldots, m}{a_i \not | x} i=1,2,,maix 的正整数 x x x 个数。数据范围 1 ≤ n , a i ≤ 1 0 9 1 \leq n, a_i \leq 10^9 1n,ai109, 0 ≤ m ≤ 10 0 \leq m \leq 10 0m10

    思路:

    • 容斥原理经典题,随便对下系数就做完了,时间复杂度 O ( 2 m log ⁡ n ) \mathcal{O}(2^m \log n) O(2mlogn),注意下最小公倍数超过有符号 32 位整型数的情况
    • [ c o n d ] = { 1 if cond is true 0 otherwise [\mathrm{cond}] =
      {1if cond is true0otherwise" role="presentation">{1if cond is true0otherwise
      [cond]={10if cond is trueotherwise
      ,所求即

    ∑ x = 1 n ∏ i = 1 m [ a i ∤ x ] = ∑ x = 1 n ∏ i = 1 m ( 1 − [ a i ∣ x ] ) = ∑ x = 1 n ∑ T ⊆ S ∏ i ∈ T ( − [ a i ∣ x ] ) = ∑ x = 1 n ∑ T ⊆ S ( − 1 ) ∣ T ∣ [ l c m i ∈ T ( a i ) ∣ x ] = ∑ T ⊆ S ( − 1 ) ∣ T ∣ ∑ x = 1 n [ l c m i ∈ T ( a i ) ∣ x ] = ∑ T ⊆ S ( − 1 ) ∣ T ∣ ⌊ n l c m i ∈ T ( a i ) ⌋ \sum_{x = 1}^{n}{\prod_{i = 1}^{m}{[a_i \not | x]}} \\ = \sum_{x = 1}^{n}{\prod_{i = 1}^{m}{\left(1 - [a_i | x ]\right)}} \\ = \sum_{x = 1}^{n}{\sum_{T \subseteq S}{\prod_{i \in T}{\left(- [a_i | x ]\right)}}} \\ = \sum_{x = 1}^{n}{\sum_{T \subseteq S}{(-1)^{|T|} [\mathrm{lcm}_{i \in T}(a_i) | x]}} \\ = \sum_{T \subseteq S}{(-1)^{|T|} \sum_{x = 1}^{n}{[\mathrm{lcm}_{i \in T}(a_i) | x]}} \\ = \sum_{T \subseteq S}{(-1)^{|T|} \left\lfloor \frac{n}{\mathrm{lcm}_{i \in T}(a_i)} \right\rfloor} x=1ni=1m[aix]=x=1ni=1m(1[aix])=x=1nTSiT([aix])=x=1nTS(1)T[lcmiT(ai)x]=TS(1)Tx=1n[lcmiT(ai)x]=TS(1)TlcmiT(ai)n

    其中 S = { 1 , 2 , … , m } S = \{1, 2, \ldots, m\} S={1,2,,m} l c m \mathrm{lcm} lcm 表示一组正整数的最小公倍数

    代码:C++

    int solution(int n, int m, std::vector<int>& vec){
        typedef long long LL;
        int result = 0;
        for(int i = 0; i < (1 << m); ++i) {
            int sgn = 1;
            LL lcm = 1;
            for(int j = 0; lcm <= n && j < m; ++j) {
                if((~i >> j) & 1)
                    continue;
                sgn = -sgn;
                lcm = lcm / std::__gcd(lcm, (LL)vec[j]) * vec[j];
            }
            if(lcm <= n)
                result += sgn * (n / lcm);
        }
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    后续

    别看了哪有下期啊。

  • 相关阅读:
    PyTorch设置可复现/重复实验
    Https加密超文本传输协议的运用
    python爬虫入门(六)BeautifulSoup使用
    java面试强基(4)
    交换机技术综述(第十一课)
    前端基础入门之JS的call、apply和argument
    如何将“龙”插入到富文本编辑器中?
    【JVM】内存模型:原子性、可见性、有序性的问题引出与解决
    DTcloud 装饰器
    @Configuration详解
  • 原文地址:https://blog.csdn.net/skywalkert/article/details/127455720