Logo admin的博客

博客

CSP2023-J2模拟1004 解题报告

2023-10-04 15:08:03 By admin

徐老师的吉祥数

题解

送分题,暴力枚举即可

标程

#include <bits/stdc++.h>
using namespace std;

bool check(int x) {
    while (x > 1){
        if (x % 3 != 0){
            return false;
        }
        x /= 3;
    }
    return true;
}
int a[1000100], len = 0;
int main() {
    int l, r, ans = 0;
    scanf("%d%d", &l, &r);
    for (int i = l; i <= r; ++i){
        if (check(i)){
            ans++;
            a[len++] = i;
        }
    }
    printf("%d\n", ans);
    for (int i = 0; i < len; ++i){
        printf("%d ", a[i]);
    }
    return 0; 
}

徐老师的二进制加法

题解

想太多容易做不出来,其实这道题直接模拟计算即可

因为每次只加一个 $2^k$ 也就是二进制上只有 $1$ 个 $1$

单次进位次数最多可以达到 $n$ 次,比如 $11111111 + 1$

但是这次以后进位次数就没了,所以平均下来进位次数最多也就是 $n + m$

所以直接模拟即可,不会超时

为了方便计算可以像高精度一样把数字倒过来存

但是注意数组得多开一些,因为最高位可能会进位

至少需要开到 $n + logm$

当然了,平时习惯好的话数组稍微大一点就可以,比如开到 $1000100$ 肯定够用了

标程

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+30;
int n,q,k,ans;
bool a[N];
char c[N];

int main(){
    scanf("%d", &n);
    scanf("%s", c);
    for(int i = 0; i < n; ++i){
        a[n - i - 1] = c[i] - '0';
    }
    scanf("%d", &q);
    while(q--){
        scanf("%d", &k);
        ans = 1;
        while(a[k]){
            a[k++] = 0;
            ans++;
        }
        a[k] = 1;
        printf("%d\n", ans);
    }
    n += log2(n) + 1;
    while(!a[n]){
        n--;
    }
    for(int i = n; i >= 0; --i){
        printf("%d", a[i]);
    }
    return 0;
}

徐老师的同桌分配

题解

很容易想到贪心就是最小的配最大的,如果只求一次结果,直接排序即可,现在的问题是每新增一组就要进行一次计算

这里我们可以考虑,因为每次只在两个数组中各新增一个数字,之前的数组如果已经有序,其实可以用插入排序的方式维护一次即可,那么可以用二分找到应该插入的位置,然后插入并计算结果,但是这样的话一次插入的复杂度依旧是 $O(n)$ 的,总体复杂度 $O(n^2)$

观察题目可以发现,这道题有一个特殊的约束,就是成绩最大只有 $100$ 分,所以为了方便维护,我们完全可以直接用桶排序来做到每次插入一个数字

这样的话配对的过程就只需要枚举 $100$ 次分数即可,那么复杂度就可以降低到 $O(100 * n)$ 即可通过

标程

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5,V=105;
int n,a[N],b[N],ta[V],tb[V],tmpa[V],tmpb[V];

int qry(){
    for(int i=1;i<=100;++i)tmpa[i]=ta[i],tmpb[i]=tb[i];
    int l=1,r=100,maxn=0,tmp;
    while(!tmpa[l])l++;
    while(!tmpb[r])r--;
    while(l<=100&&r>=1){
        tmp=min(tmpa[l],tmpb[r]);
        tmpa[l]-=tmp,tmpb[r]-=tmp;
        maxn=max(maxn,l+r);
        while(!tmpa[l] && l <= 100)l++;
        while(!tmpb[r] && r >= 1)r--;
    }
    return maxn;
}

int main(){
    scanf("%d", &n);
    for(int i=1;i<=n;++i){
        scanf("%d%d", &a[i], &b[i]);
        ta[a[i]]++,tb[b[i]]++;
        printf("%d\n", qry());
    }
    return 0;
}

徐老师的炉石传说

题解

首先对于过牌的技能来说,就是让这个牌组变成了一个环,那我们可以破坏成链,把卡组复制一遍即可解决这个问题,这个也是部分分中会考虑用到的

接着考虑正解

首先我们会发现,不管是递推也好搜索也好,打牌顺序决定了能打出的牌,如果陷入这个角度可能会很难想到正解

我们从结果倒推回来观察会发现,其实能打出去的牌一定就是在 $n$ 张牌中选了一部分

比如现在选中了 $S$ 张牌,那这 $S$ 张牌要符合一个什么要求呢?

要保证这 $S$ 张牌打出的时候不会导致其他被选中的牌被弃

我们会发现,被选中的这 $S$ 张牌的 $x_i$ 之和必然是 $\leq n$ 的,这个毋庸置疑,弃牌数量总和不可能超过 $n$

接着我们把选出的这 $S$ 张牌之间的距离记为 $dis_i$,也可以发现 $\sum{dis_i} = n$

结合以上两点,我们可以确认,在这 $S$ 张牌中至少存在一张 $x_i \leq dis_i$ 的牌

即打出这张牌时不会导致下一张牌被弃,那么打出这张牌,接着剩下 $S-1$ 张牌,又回到了上述问题

所以可以证明,只要选好了 $S$ 张牌,必然存在一定的出牌顺序使得这 $S$ 张牌被打出并且互不影响

那么现在的问题就变成了,在 $n$ 张牌中选 $S$ 张牌,保证 $\sum{x_i} \leq n$ 的情况下,求最大的 $\sum{y_i}$ 这就是一个基础 $01$ 背包问题了

标程

#include<bits/stdc++.h>
using namespace std;
int n, x[1010], y[1010], dp[1010], ans;

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++){
        scanf("%d", &x[i]);
    }
    for (int i = 1; i <= n; i++){ 
        scanf("%d", &y[i]);
    }
    for (int i = 1; i <= n; i++){
        for (int j = n; j >= 0; j--){
            if (j >= x[i]){ 
                dp[j] = max(dp[j], dp[j - x[i]] + y[i]);
            }
        }
    }
    for (int i = 1; i <= n; i++){ 
        ans = max(ans, dp[i]);
    }
    printf("%d\n", ans);
    return 0;
}

CSP2023-J2模拟1003 解题报告

2023-10-03 17:15:13 By admin

徐老师的仓鼠小队

题解

贪心,先排序,然后每三个一组,用这三个的最大值减最小值。

三只一组其实和两只一组是一个意思,就是如何分组可以让每组的距离之和最小

那自然是相邻的分一组最好

标程

#include <bits/stdc++.h>
using namespace std;
int a[100005];
int main(){
    int n;
    cin >> n;
    long long ans = 0;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
    }
    sort(a + 1, a + 1 + n);
    for(int i = 1; i <= n; i += 3){
        ans += a[i + 2] - a[i];
    }
    cout << ans << endl;
}

徐老师的羊腿出售

题解

首先很容易想到可以用前缀和来优化,对于 $[l,r]$ 的询问

设询问的两个字母分别是 $T_1, T_2$

如果枚举一个左端点 $a[i] == T_1$ 的话,$a[i]$ 能组成的方案数就是 $sum[T_2][r] - sum[T_2][i-1]$

但是这样的操作复杂度依旧是 $O(n^2)$,只有 $40$ 分

那么观察数据范围可以发现,必须是 $O(n)$ 或者 $O(nlogn)$

并且仔细思考为什么这道题是字母而不是数字

字母只有 $26$ 个,所以我们可以考虑能否提前处理出任意两个字母两两对应的二次前缀和

也就是说处理出 $f[T][i]$ 表示到 $i$ 为止字母 $T$ 的前缀和

然后即可处理出 $sum[T_1][T_2][i]$ 表示到 $i$ 为止的 $T_1$ 对应 $T_2$ 的方案数前缀和

那么对于每次询问,即可二分出最左侧的 $T_2$ 位置 $ql$ 和最右侧的 $T_2$ 位置 $qr$

那么就可以用 $ans = sum[T_2][T_1][qr] - sum[T_2][T_1][ql - 1]$ 来计算出区间 $[l,r]$ 内的所有 $T_2$ 对于 $T_1$ 组合的方案数

再减去 $[l,r]$ 区间内的 $T_2$ 对于 $1 \sim l - 1$ 中 $T_1$ 组合的方案数即可

最后答案为 $ans = sum[T_2][T_1][qr] - sum[T_2][T_1][ql - 1] - cnt * f[l-1][T_1]$

标程

#include<bits/stdc++.h>
using namespace std;
int n,q;
string S,T;
const int MN=2e5+5;
vector<int>pos[26],sum[26][26];
int f[MN][26];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>q;
    cin>>S;
    for(int i=1; i<=n; i++) {
        int t=(int)(S[i-1]-'a');
        for(int x=0; x<26; x++){
            f[i][x]=f[i-1][x];
            sum[t][x].push_back(f[i][x]);
        } 
        pos[t].push_back(i);
        f[i][t]++;
    }
    for(int t=0; t<26; t++) {
        for(int x=0; x<26; x++) {
            for(int j=1; j<sum[t][x].size(); j++){
                sum[t][x][j]+=sum[t][x][j-1];
            }
        }
    }
    while(q--) {
        int l,r;
        cin>>l>>r>>T;
        int a=T[0]-'a',b=T[1]-'a';
        int ql=lower_bound(pos[b].begin(),pos[b].end(),l)-pos[b].begin();
        int qr=upper_bound(pos[b].begin(),pos[b].end(),r)-pos[b].begin()-1;
        if(ql>qr) {
            printf("0\n");
            continue;
        }
        int cnt=qr-ql+1;
        printf("%d\n",sum[b][a][qr]-(ql==0?0:sum[b][a][ql-1])
            -f[l-1][a]*cnt);
    }
    return 0;
}

徐老师的数字拼接

题解

观察数据范围可以发现,最大只会到 $99999999$,并且 $n$ 很小,直接暴力 $dfs$ ,最后去重计数即可

标程

#include<iostream>
#include<cstdio>
#include<cassert>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 15,K = 5;
int n,k;
int a[N];
bool vis[N];
int b[K];
int uppw10(int x) {
    int cnt = 1;
    while(x > 0){
        x/=10;
        cnt*=10;
    }
    return cnt;
}
vector<int>v;
void dfs(int step) {
    if(step > k) {
        int val = 0;
        for(int i= 1 ; i <= k; i++)
            val = val * uppw10(b[i]) + b[i];
        v.push_back(val);
        return;
    }
    for(int i = 1; i <= n; i++){
        if(!vis[i]) {
            vis[i] = true;
            b[step] = a[i];
            dfs(step + 1);
            vis[i] = false;
        }
    }
    return;
}
int main() {
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
    }
    dfs(1);
    sort(v.begin(),v.end());
    int ans=0;
    for(int i = 0; i < (int)v.size(); i++){
        if(i==0||v[i]!=v[i-1]){
            ans++;
        }
    }
    printf("%d",ans);
    return 0;
}

徐老师的倒水计划plus

题解

可以发现,最后保留 $m$ 个玻璃杯,这些玻璃杯的水能被保留,其他玻璃杯的水就被保留一半,然后和这 $k$ 个玻璃杯的容量总和求个 $min$ 就可以了

很容易想到的就是贪心,但是显然贪心是错误的,因为你并不知道应该选容量越大的还是原本水越多的

所以明显是个类似于背包的 dp 问题

设被选中的 $m$ 个杯子的容量总和为 $a$, 水量总和为 $b$,剩下 $n - m$ 个杯子的水量之和为 $c$

答案即为 $min(a, b + c/2)$,这里我们可以发现当 $a,b$ 确认时 $c$ 是可以直接计算的

那么我们可以设 $dp[i][j][a]$ 为前 $i$ 个杯子,选了 $j$ 个,被选玻璃杯容量和为 $a$ 时的最大水量

那么我们在转移的时候只要考虑第 $i + 1$ 个杯子选或者不选即可完成转移

标程

#include <bits/stdc++.h>
using namespace std;
int a[55], b[55], dp[55][5010];

int main() {
    int n, s = 0;
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i] >> b[i], s += b[i];
    memset(dp, -0x3f, sizeof(dp));
    dp[0][0] = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = i; j; --j)
            for (int k = a[i]; k <= 5000; ++k)
                dp[j][k] = max(dp[j][k], dp[j - 1][k - a[i]] + b[i]);
    for (int i = 1; i <= n; ++i) {
        double ans = 0;
        for (int j = 1; j <= 5000; ++j)
            ans = max(ans, min(1.0 * j, 0.5 * (s + dp[i][j])));
        printf("%.1f ", ans);
    }
    return 0;
}

2023初赛模拟卷(3)解析

2023-09-12 19:14:08 By admin

一、单项选择题

  1. A。计算机缺少CPU将无法正常启动。
  2. A。不妨将单位全都转成MB:36000 KB ≈ 35.2 MB,33 MB = 33 MB,0.03 GB = 30.72 MB,36500000 B ≈ 34.8 MB,所以 36000 KB 最大。
  3. C。不妨设状态 $f_i$ 表示所有高度 $\leq i$ 的二叉树的不同形态树,则可得推导公式为 $f_i=(f_{i-1}+1)^2$,所以可得 $f_1=1,f_2=4,f_3=25$,所以高度为 $3$ 的二叉树的不同形态数为 $f_3-f_2=25-4=21$。
  4. D。对于出栈序列 $e, d, b, c, a$,因为入栈序列是 $a,b,c,d,e$,在 $b,c$ 均未出栈的情况下,$e,d$ 先出栈,说明在 $e,d$,出栈时 $c$ 在 $b$ 的顶端,所以必然 $c$ 先出栈,与出栈序列矛盾,所以选项 D 错误。
  5. B。二进制数 $11001.1=$ 十进制 $1x24+1x23+0x22+0x21+1x20+1x2-1=16+8+0+0+1+0.5=25.5$。
  6. C。相同深度的二叉树中满二叉树包含的叶结点个数最多。一棵深度为 $h$ 的满二叉树具有 $2^{h-1}$ 个叶结点,因为深度为 $11$ 的满二叉树包含的叶结点个数为 $2^{10}=1024$,深度为 $12$ 的满二叉树包含的叶结点个数为 $2^{11}=2048$,且 $1024<2023 \leq 2048$,所以包含 $2023$ 个叶结点的二叉树深度至少为 $12$。

  7. A。八进制的一位对应二进制的三位,所以八进制数的后 $99$ 位均对应二进制的 $3$ 位,总位数为 $99x3=297$,最高位可能对应二进制的 $1 \sim 3$ 位(八进制 $1$ 对应二进制 $1$,八进制 $2$ 对应二进制 $10$,八进制 $3$ 对应二进制 $11$,八进制 $4$ 对应二进制 $100$,八进制 $5$ 对应二进制 $101$,八进制 $6$ 对应二进制 $110$,八进制 $7$ 对应二进制 $111$),所以一个 $100$ 位的八进制数转成二进制数可能是 $298、299$ 或 $300$ 位。

  8. B。对于 $2000$ 来说,它只包含两个不同的质因子—— $2$ 和 $5$。所以与 $2000$ 互质的数是即不包含因数 $2$ 也不包含因数 $5$的 所有数字,$2000$ 以内,包含因数 $2$ 的数共有 $2000/2=1000$ 个,包含因数 $5$ 的数共有 $2000/5=400$ 个,同时包含因数 $2$ 和 $5$ 的数(即包含因数 $10$ 的数)有 $2000/10=200$ 个,所以可得:包含因数 $2$ 或 $5$ 的数共有 $1000+400-200=1200$ 个,所以 $2000$ 以内与 $2000$ 互质的数的个数为 $2000-1200=800$ 个。
  9. A。 x & -x 可得到 $x$ 在二进制表示下最低一位 $1$ 对应的整数。
  10. A。可以将 $4$ 轮看成一个循环,每个循环执行完后,$x$ 将会 $-2$,$y$ 将会 $+2$,前 $2020$ 轮恰好 $2020/4=550$ 个循环,所以前 $2020$ 轮之后的坐标是 $(-1010,1010)$,第 $2021$ 轮 $x$ 增加了 $2021$(变成 $-1010+2021=1011$),第 $2022$ 轮 $y$ 减小了 $2022$(变成 $1010-2022=-1012$),第 $2023$ 轮 $x$ 减少 $2023$(变成 $1011-2023=-1012$)$,所以最终的坐标是 $(-1012,-1012)$。
  11. D。相当于从 $5$ 行中选两行,再从 $5$ 列中选 $2$ 列,其中 $2$ 个方格之间有 $A_2^2$ 种排列,所以总的方案数为 $C_5^2⋅C_5^2⋅A_2^2=200$。
  12. C。分析代码可得 C 错误。在比赛时考虑时间因素,也可采用“套例子”的方法,比如:设 $x=3,y=2$(期望的结果为 $2$),将其带入四个选项,可得A、B、D的结果均为 $2$,而 C 的结果为 $1$,所以选C。
  13. B。$4$ 个人之间的排列数为 $A_4^4$ ,可以想象将连续的三个空位看成一组,另一个空位看成一组,将两组空位插入 $4$ 个人之间的五个位置,方案数有 $C_5^2$ ,两组空位之间的排列数为 $A_2^2$ ,所以总的方案数为 $A_4^4⋅C_5^2⋅A_2^2=480$。
  14. B。画图即可。
  15. C。图灵奖是计算机科学领域的最高奖。

二、阅读程序

第一小题

  1. 错误。也可包含小写英文字母,因为程序中有将小写英文转成大写英文字母的实现。
  2. 正确。程序实现的功能就是将输入的字符串中的字符全部转成大写英文字母并从小到大排序后输出,所以输出的字符串长度与输入的字符串长度相同。
  3. 正确 rUiBA时,输出应为 abiru
  4. 错误。当输入为 niceToMeetYou,输出应为 ceeeimnoottuy
  5. B。当输入的字符串是升序的且仅由小写英文字母组成时,输出的字符串与输入的字符串一样。
  6. C。当输入为 codeInRUIBA 时对应的输出结果为 abcdeiinoru

第二小题

本程序使用二分查找算法寻找 $x$ 在数组 $a$ 中对应的下标 $p$(若 $x$ 在数组中不存在则 $p=-1$),同时使用 $count$ 记录二分查找的次数。

  1. 错误。解析:变量 $x$ 并不对应数组下标,所以 $x$ 的值并不会造成数组越界。
  2. 正确。解析:因为 $p$ 对应数组 $a$ 的下标,所以若找到 $a[p]==x$,则 $p$ 的值介于 $0 \sim 14$ 范围内,若没有找到,则 $p=-1$,所以 $p$ 的值不会超过 $14$。
  3. 正确。解析:变量 $count$ 的值对应二分查找的次数,对于长度为 $15$ 的数组 $a$,二分查找的次数不会超过 $⌈log_215⌉=4$。
  4. C。解析:$a[2]==5$。
  5. A。解析:$a$ 数组中不存在任何一个元素等于 $12$,所以输出的第一行为 $-1$。
  6. D。解析:一共二分查找了 $4$ 次,对应的 $mid$ 及 $a[mid]$ 分别为 $a[7]=22,a[11]=62,a[9]=45,a[8]=37$。

第三小题

  1. 正确。 设 $len$ 表示 $s$ 的二进制表示中共有多少位为 $1$,而 $cnt=2^{len}-1$,而具有 $len$ 位 $1$ 的所有二进制整数中数值最小的即为 $2^{len}-1$,所以 $cnt \le s$ 成立,或者换个简单的思路,i 永远在变小,那么最起码也是每次 $-1$,所以 $i$ 的变化次数最多就是 $s$
  2. 错误。 $bitcount(x)$ 函数计算的是 $x$ 的二进制表示中共有多少位为 $1$。
  3. 错误。数组下标对应的是 $i$ 的二进制数表示中 $1$ 出现的位数,对于 $1 \sim 10^9$ 范围内的整数,其二进制表示中 $1$ 出现的位数不会超过 $100$,所以不会发生数组越界。
  4. C。$(23)_{10}=(10111)_2$,所以 $len=4$,得 $cnt=2^{len}-1=15$。
  5. D。 $c[1]=8+2+1=11,c[2]=10+9+3=22,c[3]=11$,得 $ans=22$。
  6. B。通过分析代码可得 $c[i]=(C_{len}^i⋅i)/len⋅127$,所以 $c[1] = 127,c[2] = 762,c[3] = 1905,c[4] = 2540,c[5] = 1905,c[6] = 762,c[7] = 127$,得 $ans=2540$。

三、完善程序

第一小题

  1. B。当 $d < f(y,m)$ 时, $y$ 年 $m$ 月 $d$ 日的下一天是 $y$ 年 $m$ 月 $d+1$ 日。
  2. C。当 $d < f(y,m)$ 不成立(即 $d==f(y,m)$ )时,说明是该月的最后一天,此时若 $m < 12$,则 $y$ 年 $m$ 月 $d$ 日的下一天是 $y$ 年 $m+1$ 月 $1$ 日。
  3. C。当 $d < f(y,m)$ 和 $m < 12$ 均不成立是,说明 $m==12$ 且 $d==31$,说明是 $y$ 年的最后一天,所以它对应的下一天是 $y+1$ 年 $1$ 月 $1$日。
  4. A。
  5. A。

第二小题

  1. C。该空与下一空与快速排序的逻辑相同,易得第 (1) 空填 $j--$,也可以联系上下文对称来选择
  2. B。理由同上,当然这里可以发现,第 $11$ 行 $a[i] = a[j]$ 后,$a[j]$ 的值在逻辑上已经空出来了,这里肯定是给 $a[j]$ 赋值
  3. D。循环操作结束后能保证区间 $[l,i-1]$ 范围内的数都 $>= a[i]$,区间 $[i+1,r]$ 范围内的数都 $<= a[i]$,所以若 $i-l+1==k$,则说明 $a[i]$ 恰为区间 $[l,r]$ 范围内第 $k$ 大的数。
  4. C。若 $i-l+1 > k$,则进区间 $[l,i-1]$ 继续查找第 $k$ 大的数。
  5. C。若 $i-l+1 < k$,则区间 $[l,r]$ 范围内第 $k$ 大的是应该在区间 $[i+1,r]$ 范围内,但是因为区间 $[l,i]$ 范围内的 $i-l+1$ 个数都大于等于区间 $[i+1,r]$ 范围内的数,所以应去区间 $[i+1,r]$ 范围内找第 $k-(i-l+1)=k+l-i-1$ 大的数。

2023初赛模拟卷(2)解析

2023-09-12 19:08:49 By admin

一、单项选择题

  1. A。 C 语言是面向过程的编程语言;C++、Python、Java均为面向对象的编程语言。
  2. C。 $1GB = 1024MB = 1024 * 1024 KB = 1024 * 1024 * 1024 B = 2^30B$。
  3. A。 八进制数 $356.433$ 对应的十六进制整数是 $EE.AD8$。
  4. B。 二进制数 $00101100+00011101=01001001$。
  5. A。 栈的特点是先进后出,后进先出(First In Last Out, Last In First Out)。
  6. D。 $POP3$(邮局协议的第3个版本)、$SMTP$(简单邮件传输协议)、$IMAP$(交互式邮件存取协议)均为电子邮件中常用的协议;$P2P$对等网络是一种网络结构,与电子邮件传输无关。
  7. C。 因要求男女相间,则男生和女生的位置共有如下两种不同方案: • 男生在第 $1、3、5$ 个位置,女生在第 $2、4、6$ 个位置; • 或者女生在第 $1、3、5$ 个位置,男生在第 $1、3、5$ 个位置。 其次,男生之间的排列方案数为 $A_3^3$ 种,女生之间的排列方案数为 $A_3^3$ 种,所以总的排列方案数为 $C_2^1×A_3^3×A_3^3=72$。

  8. C。 包含 $100$ 个节点的二叉树中共有 $99$ 条边,对于任意一个节点,它若要包含 $2$ 个子节点,则需要 $2$ 条边分别连向它的两个子节点,所以最多有 $99/2=49$ 个节点具有 $2$ 个子节点。

  9. C。 快速排序算法的平均时间复杂度为 $O(nlogn)$。
  10. C。 • 以 A 开头共有 $6$ 个不同的子串,分别是 A、AB、ABB、ABBC、ABBCC、ABBCCC; • 以 B 开头共有 $5$ 个不同的子串,分别是 B、BB、BBC、BBCC、BBCCC、BC、BCC、BCCC; • 以 C 开头共有 $3$ 个不同的子串,分别是 C、CC、CCC。 所以共有 $6+8+3=17$ 个不同的非空子串。
  11. C。 $3570$ 和 $2023$ 的最大公约数是 $119$。
  12. D。基础二分查找的要求,元素有序,且能随机访问。
  13. B。不妨想思考 $0 \sim 99$ 这 $100$ 个数中包含 $2$ 的数的个数,可以发现,除了 $20 \sim 29$ 这 $10$ 个数以外,还存在 $2、12、32、42、52、62、72、82、92$ 这 $9$ 个数是包含 $2$ 的,所以 $0 \sim 99$ 范围内共有 $10+9=19$ 个包含 $2$ 的数,据此可以推导出 $100 \sim 199$ 范围内包含 $2$ 的数的个数也为 $19$,对于 $300 \sim 399,400 \sim 499,...,900 \sim 999$ 也可以发现同样的规律,但是 $200 \sim 299$ 这 $100$ 个数的百位都是 $2$, 所以这 $100$ 个数都是包含 $2$ 的数。据此可以推导出 $0 \sim 999$ 范围内包含 $2$ 的数的个数 $=100+19x9=271$,同理可得 $1000 \sim 1999$ 范围内包含 $2$ 的数的个数也为 $271$ 。而 $2000 \sim 2023$这 $24$ 个数都是包含 $2$ 的数(因为千位为 $2$),所以 $0\ sim 2022$ 范围内包含 $2$ 的数的个数为 $271x2+24=566$ ,因 $0$ 不包含 $2$,所以可得: $1 \sim 2023$ 范围内的整数中包含 $2$ 的数的个数为 $566$。
  14. B。根据函数的推导公式可得 $solve(5,6)$ 的结果为 $210$。
  15. C。解析:冯·诺依曼体系结构的核心内容是采用存储程序和程序控制原理。

二、阅读程序

第一小题

  1. 正确。解析:当 $n=1000$ 时,会使用到下标为 $1000$ 的数组元素 $a[1000]$,而数组的元素下标从 $0$ 到 $999$,所以会发生数组越界。
  2. 错误。解析:解决本题最简单的办法就是找一个“反例”,可以发现,当 $n=3$ 时,$a[1]=2,a[2]=3,a[3]=2$,并不是一个 $1 \sim n$ 的排列。
  3. 正确。解析:输入为 $5$ 时,输出的第一行为 $3$。
  4. 错误。解析:输入为 $7$ 时,输出的第一行为 $1$。
  5. D。解析:该程序的目的是将 $n$ 个数的最小值交换到 $a[1]$,将第二小的值交换到 $a[2]$,其中可能存在多个数的值均为最小值的情况,此时 $a[1]==a[2]$,如果数字都不相同则第一个数字小于第二个数字,所以答案是不确定
  6. C。解析:当 $n=20$ 时,$a$ 数组中的 $20$ 个元素分别为 $11,14,9,4,19,14,9,4,19,14,9,4,19,14,9,4,19,14,9,4$,其中次小值为 $4$。

第二小题

  1. 正确。循环过程中时刻能保证对于任意 $a[i]$,若 $i>1$,则 $a[i/2]<=a[i]$,所以 $a[1]$ 最小。
  2. 错误。是能保证 $a[i/2]$ 和 $a[i]$之间的大小关系,所以数组中一些元素的大小关系是无法确定的,比如 $a[2]$ 和 $a[3]$,所以程序并不能保证 $a[1] \sim a[n]$ 是升序的。
  3. 正确。可以发现,当 $n$ 个元素按逆序输入时,输出的 $cnt$ 是最大的,不妨设 $a[1]=10,a[2]=9,...,a[10]=1$,可以发现:输入 $a[1]$ 后交换了 $0$ 次、输入 $a[2]$ 后交换了 $1$ 次、输入 $a[3]$ 后交换了 $1$ 次、输入 $a[4]$ 后交换了 $2$ 次、输入 $a[5]$ 后交换了 $2$ 次、输入 $a[6]$ 后交换了 $2$ 次、输入 $a[7]$ 后交换了 $2$ 次、输入 $a[8]$ 后交换了 $3$ 次、输入 $a[9]$ 后交换了 $3$ 次、输入 $a[10]$ 后交换了 $3$ 次。所以总共交换了 $0+1+1+2+2+2+2+3+3+3=19$ 次。
  4. 错误。当 $n$ 个元素按逆序输入时,输出的 $cnt$ 最大,同时对于第 $i$ 个元素 $a[i]$,交换次数为 $⌊log2i⌋-1$ 次。所以: 对于第 $1$ 个数(共1个数),各交换了0次; 对于第 $2 \sim 3$ 个数(共2个数),各交换了 $1$ 次; 对于第 $4 \sim 7$ 个数(共4个数),各交换了 $2$ 次; 对于第 $8 \sim 15$ 个数(共8个数),各交换了 $3$ 次; 对于第 $16 \sim 31$ 个数(共16个数),各交换了 $4$ 次; 对于第 $32 \sim 63$ 个数(共32个数),各交换了 $5$ 次; 对于第 $64 \sim 100$ 个数(共37个数),各交换了 $6$ 次。 总的交换次数为 $1×0+2×1+4×2+8×3+16×4+32×5+37×6=480$次 $>450$ 次。
  5. D。输入 $a[3](2)$后交换了1次,输入 $a[4](4)$ 后交换了 $1$ 次,输入 $a[5](1)$ 后交换了 $2$ 次,共交换了 $4$ 次。
  6. A。只在输入 $a[3](1)$ 后交换了 $1$ 次。

第三小题

  1. 错误。 $init()$ 函数调用结束后,$m$ 的值为 $1 \sim 1000$ 范围内素数的个数,虽然我们不能很快得出这个结果,但是容易发现 $1 \sim 1000$ 范围内素数的个数肯定比非素数的个数要少,所以 $m$ 不可能是 $500$ 。
  2. 正确。 $init()$ 函数调用结束后,所以素数 $i$ 对应的 $f[i]$ 均为 $0$,所有非素数 $i$ 对应的 $f[i]$ 均为 $1$。因为 $103$ 是素数,所以 $f[103]=0$。
  3. 正确。 $g[1000]$ 表示的是 $1 \sim 1000$ 中非素数的个数, $m$ 表示的是 $1 \sim 1000$ 中素数的个数,所以 $g[1000]+m$ 等于 $1000$。
  4. B。因为 $123$ 不是素数,所以 $f[123]=1$。
  5. C。 $g[30]$ 表示 $1 \sim 30$ 范围内非素数的个数,$1 \sim 30$ 范围内共有 $20$ 个非素数,所以 $g[30]=20$。
  6. B。 $p[i]$ 表示第 $i$ 个素数,我们可以依次列举出前 $25$ 个素数:$2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97$,可得第 $25$ 个素数 $p[25]=97$。

三、完善程序

第一小题

  1. A。解析:喝了 $n$ 瓶汽水,已喝汽水总量增加$n(s+=n)$。
  2. A。解析:喝了 $n$ 瓶汽水,瓶盖总量增加 $n(t+=n)$。
  3. D。解析:用 $t$ 个瓶盖最多可兑换 $t/k$ 瓶汽水,所以 $n$ 更新为 $t/k$。
  4. B。解析:兑换尽可能多的汽水后,剩余瓶盖数量为 $t%k$。
  5. A。解析:只要能对话至少 $1$ 瓶汽水$(n>0)$,就继续循环。

第二小题

  1. C。解析:第 $i$ 小的应与第 $x+1-i$ 大数(即 $a[n-x+i]$)的比较。
  2. C。解析:根据输入是从 $a[1]$ 输入 $a[n]$,所以排序也是给 $a[1]$ 到 $a[n]$ 排序,对应的 $sort$ 函数应写为 $sort(a+1, a+n+1)$。
  3. C。解析:根据二分的写法,可知判断的区间范围是 $[l,r]$,所以二分的判断条件是 $l <= r$。
  4. B。解析:因为 $check(x)$ 判断能够凑成 $x$ 对,所以当条件成立时应让 $l = mid + 1$ 以寻找更大的答案;当条件不成立是应让 $r = mid - 1$ 以寻找更小的答案。
  5. C。

2023初赛模拟卷(1)解析

2023-09-12 19:06:51 By admin

一、单项选择题

  1. B。$8$ 位占用 $1$ 字节,所以 $64$ 位占用 $8$ 字节。
  2. C。折半查找的最大比较次数为 $⌈log21000⌉=10$次。其中 $⌈x⌉$ 表示 $x$ 向上取整的结果。
  3. C。$128$ 位等价于 $128 / 8 = 16B$ ,两张照片占用的空间为 $2×2048×2048×16B=128MB$。
  4. B。$(1011.11)2=1×23+0×22+1×21+1×20+1×2-1+1×2-2=8+0+2+1+0.5+0.25=11.75$。
  5. B。无向图中的顶点度数之和$=边数 \times 2=20+0 \times 24=40$,所以顶点个数 $= 40 / 4 = 10$。
  6. A。 徐老师(洗菜) 第 $0-15$ 分钟 第 $15-30$ 分钟 第 $30-45$ 分钟 爸爸(切菜) 第 $15-25$ 分钟 第 $30-35$ 分钟 第 $45-55$ 分钟 妈妈(烧菜) 第 $25-45$ 分钟 第 $45-65$ 分钟 第 $65-85$ 分钟 每位家庭成员完成工作的最快时间如上标所示,所以做完三道菜的最短时间为 $85$ 分钟。

  7. C。根据表达式 (a + b) * (c + d) 构造表达式树,据此可得后缀表达式为 a b + c d + *

  8. C。对于字符串 hetao: • 以 h开头的子串有 $5$ 个,分别是 h、he、het、heta、hetao(字符串本身也算其子串); • 以 e 开头的子串有 $4$ 个,分别是 e、et、eta、etao; • 以 t 开头的子串有 $3$ 个,分别是 t、ta、tao; • 以 a 开头的子串有 $2$ 个,分别是 a、ao; • 以 o 开头的子串有 $1$ 个,是 o。 初次以外,题目中没有强调非空子串,所以空串也应该算其子串。所以字符串 hetao 的子串个数为 $5+4+3+2+1+1=16$。

  9. C。按照 入栈、入栈、入栈、出栈、出栈、入栈、入栈、入栈、出栈、入栈、出栈、出栈、出栈、出栈 的顺序,入栈序列 a,b,c,d,e,f,g 对应的出栈序列为 c,b,f,g,e,d,a

  10. D。(x∨y)∧(y∨z)=(true)∧(true)=true
  11. B。$8$ 层的满二叉树的节点个数为 $2^0+2^1+...+2^7=2^8-1=255$。
  12. C。符号位为 $1$,可推出是负数,所以可以按照 补码=反码+1 的规律推出补码 $10101010$ 对应的反码为 $10101001$,再将反码的数值位取反得到原码为 $11010110$。
  13. C。画图即可
  14. B。$84$ 的哈希值为 $84 mod 11 = 7$,存放在地址空间 $7$ 中,$25$ 的哈希值为 $25 mod 11 = 3$,存放在地址空间 $3$ 中,$38$ 的哈希值为 $5$,$57$的哈希值为 $2$,$71$ 的哈希值为 $71 mod 11 = 5$,此时发现 $5$ 已经存放了数据了,那么查看 $6$ 能否存放,发现是空的,则存放在 $6$
  15. B。 $3$ 名医生分配到 $3$ 所学校课视为 $3$ 个人的排列 $A_3^3$,从 $6$ 名护士中选出 $2$ 名护士去第 $1$ 所学校的方案数为 $C_6^2$,从剩余 $4$ 名护士中选出 $2$ 名护士去第 $2$ 所学校的方案数为 $C_4^2$,剩余 $2$ 名护士去第 $3$ 所学校的方案数为 $C_2^2$,所以总的方案数为 $A_3^3×C_6^2×C_4^2×C_2^2=540$。

二、阅读程序

第一小题

  1. 错误。输入的字符串可包含出字母和数字字符以外的字符,只不过程序并不会处理。
  2. 错误。程序运行时不会发生错误,因为 s[n]=='\0',不满足任何一个 if 条件。
  3. 正确。因为输入的字符串可能包含 $ASCII$ 码大于 'z' 的 $ASCII$ 码的字符,此时对于 $cnt$ 数组来说,可能发生数组越界。
  4. 正确。因为数字字符对应的是 $cnt[0]$ 到 $cnt[9]$,所以当输入的字符串全由数字字符组成时,结果必然在 $0 \sim 9$ 范围内。
  5. C。字符串 ABCDcbaAcDbC 中字符 'C/c' 出现的次数最多( $4$次),对应的下标为 $2$ 。
  6. C。字符串 a2B3233CCDC 中字符 'C/2' 出现的次数最多( 'C'出现了 $3$ 次,'2' 出现了 $2$ 次,共 $5$ 次),对应的下标为 $2$。

第二小题

  1. 错误。由于存在大量的重叠问题,所以计算 $f(n,m)$ 的时间复杂度远超 $O(nm)$。
  2. 错误。即使去掉第 $4$ 行的代码,程序在 $m=1$ 时会返回 $n$,在 $n < 1$ 时会返回 $0$,所以不会无限递归下去。
  3. 错误。即使同时去掉第 $4$ 行和第 $5$ 行的代码,程序在 $n < 1$ 和 $m < 1$ 时也会返回 $0$,所以不会无限递归下去。
  4. D。$f(5,6)=175$。
  5. B。$f(100,2)=f(1,1)+f(2,1)+f(3,1)+...+f(99,1)=1+2+3+...+99=4950$。
  6. B。需要注意的是,当去掉第 $4$ 行的代码后,边界条件是:当 $m=0$ 时返回 $n$,否则当 $n=1$ 时,$f(n,m)$ 返回 $0$,据此计算得到 $f(3,6)=7$。

第三小题

本题基于倍增思想构造 $ST$ 表求解 $RMQ$ 问题,输出 $s[x]$ 到 $s[y]$ 中 $ASCII$ 码最大的那个字符。

  1. 错误。解析:当输入为 CGFCDBAE 2 6 时,输出为 F。需要注意的是——数组下标从 $0$ 开始计算。
  2. 正确。解析:因为 $f$ 数组的第二维只开了 $8$ 的大小,所以虽然第一维的大小是 $1000$,但是只要 $n$ 大于 $2^7=128$ 时,计算 $f$ 数组元素时就会发生数组越界。
  3. 错误。解析:因为 $ST$ 表需要先计算所有的 $f[i][0]$,再由f[i][0]推导出所有的 $f[i][1]$,再由 $f[i][1]$ 推导出所有的 $f[i][2]$,所以代码第 $19$ 行和 $20$ 行的代码的顺序不能改变。
  4. B。解析:函数 $Log2(x)$ 返回的是 $⌊log2x⌋$的结果(其中 $⌊x⌋$ 表示 $x$ 向下取整),所以 $Log2(x)$ 的返回值是 $3$。
  5. D。解析: HETAOACCEP 中 $ASCII$ 码最大的字符是 T
  6. B。解析: InHETAO 中 $ASCII$ 码最大的字符是 n,需要注意的是小写英文字母的 $ASCII$ 码都大于大写英文字母。

三、完善程序

第一小题

  1. A。需要将循环变量 $i$ 进行初始化。
  2. C。数组下标从 $0$ 到 $n-1$,所以循环的条件是 $i < n$。
  3. C。程序的目的是不断地比较 $a[i-1]$ 和 $a[i]$,若 $a[i-1] > a[i]$ 则交换 $a[i-1]$ 和 $a[i]$,所以当 $a[i-1]<=a[i]$,则不用交换,直接 $i++$ 即可。
  4. B。第 $14 \sim 16$ 行使用三步交换法交换 $a[i]$ 和 $a[i-1]$ 的数值。
  5. C。在 (5) 处填写 $i=0$ 或 $i=1$ 程序仍然能够正常运行,但是通过分析可以发现,若设 $i'$ 为目前 $i$ 变化成的最大的值,则 $a[1] \sim a[i'-1]$ 已满足升序,所以及时将 $i$ 重置为 $0$ 或 $1$,它仍会不停自增到 $i'$ 并根据条件进行交换与自减操作,所以执行 $i=0$ 或 $i=1$ 的计算量总是大于等于执行 $i--$ 的计算量,所以执行 $i--$ 是最快的方案。执行 $i++$ 是错误的。

第二小题

通过分析问题代码,可得本题的思路是先找到满足 $a[i-1] > a[i]$ 的最小的下标 $i$,然后从 $a[i]$ 到 $a[n-1]$ 中找到最大的满足 $a[j] < a[i-1]$ 的 $a[j]$,并将 $a[i-1]$ 与 $a[j]$ 交换,然后对 $a[i] \sim a[n-1]$ 从大到小排序,即实现求解 $a$ 的上一个排列。

  1. B。通过上面的分析可得 $cmp$ 函数想要实现的效果是 从大到小排,所以函数将返回 $a > b$。
  2. A。若循环结束时 $i$ 的值为 $0$,说明序列是升序的,一个升序的排列本身就是最小的排列,没有上一个排列。
  3. C。因为 $a[i]$ 到 $a[n-1]$ 是升序的,且 $a[i] < a[i-1]$,所以从 $i$ 到 $n-1$ 找到最后一个满足 $a[j] < a[i-1]$ 的 $a[j]$ 即为要交换的那个数,且 $a[j+1] > a[i-1]$。
  4. B。需要将 $a[i]$ 到 $a[n-1]$ 中最大的那个 $< a[i-1]$ 的数(即 $a[j]$)找出来与 $a[i-1]$ 交换,所以是将 $a[i-1]$ 与 $a[j]$ 进行交换。
  5. C。需要将 $a[i]$ 到 $a[n-1]$ 从大到小排序。
admin Avatar