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;
}

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;
}

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;
}

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}] = \begin{cases}1 & \text{if cond is true} \\ 0 & \text{otherwise}\end{cases} [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;
}

后续

别看了哪有下期啊。

你可能感兴趣的:(总结,算法)