第十二届蓝桥杯全国软件和信息技术专业人才大赛软件组模拟赛(一)

本文探讨了多种算法和数据结构的应用,包括完全二叉树的权值计算、外卖店优先级管理、数组元素去重策略、糖果购买策略以及组合数问题的解决。通过实例分析和算法讲解,深入解析了如何利用编程技巧高效地处理这些问题。
摘要由CSDN通过智能技术生成

试题 F: 完全二叉树的权值

时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分
【问题描述】
给定一棵包含 N 个节点的完全二叉树,树上每个节点都有一个权值,按从
上到下、从左到右的顺序依次是 A1, A2, · · · AN,如下图所示:
现在小明要把相同深度的节点的权值加在一起,他想知道哪个深度的节点
权值之和最大?如果有多个深度的权值和同为最大,请你输出其中最小的深度。
注:根的深度是 1。
【输入格式】
第一行包含一个整数 N。
第二行包含 N 个整数 A1, A2, · · · AN 。
【输出格式】
输出一个整数代表答案。
【样例输入】
7
1 6 5 4 3 2 1
【样例输出】
2
【评测用例规模与约定】
对于所有评测用例,1 ≤ N ≤ 100000,−100000 ≤ Ai ≤ 100000。

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

int a[100005];
int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    long long sum = 0, minp = 1;
    long long maxn = LONG_LONG_MIN, k = 1, p = 0;
    int t = 1;
    for (int i = 0; i < n; ++i) {
        p += a[i];
        sum++;
        if (sum == k || i == (n - 1)) {
            sum = 0;
            if (p > maxn) {
                minp = t;
                maxn = p;
            }
            k <<= 1;
            p = 0;
            t++;
        }
    }
    cout << minp << endl;
    return 0;
}

题解:枚举每个深度能容纳几个权值,一个一个比较就好了,可以整成一边输入一边处理的样子

试题 G: 外卖店优先级

时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分
【问题描述】
“饱了么”外卖系统中维护着 N 家外卖店,编号 1 ∼ N。每家外卖店都有
一个优先级,初始时 (0 时刻) 优先级都为 0。
每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减
到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。
如果某家外卖店某时刻优先级大于 5,则会被系统加入优先缓存中;如果
优先级小于等于 3,则会被清除出优先缓存。
给定 T 时刻以内的 M 条订单信息,请你计算 T 时刻时有多少外卖店在优
先缓存中。
【输入格式】
第一行包含 3 个整数 N、M 和 T。
以下 M 行每行包含两个整数 ts 和 id,表示 ts 时刻编号 id 的外卖店收到
一个订单。
【输出格式】
输出一个整数代表答案。
【样例输入】
2 6 6
1 1
5 2
3 1
6 2
2 1
6 2
【样例输出】
1
【样例解释】
6 时刻时,1 号店优先级降到 3,被移除出优先缓存;2 号店优先级升到 6,
加入优先缓存。所以是有 1 家店 (2 号) 在优先缓存中。
【评测用例规模与约定】
对于 80% 的评测用例,1 ≤ N, M, T ≤ 10000。
对于所有评测用例,1 ≤ N, M, T ≤ 100000,1 ≤ ts ≤ T,1 ≤ id ≤ N。

#include <algorithm>
#include <cstdio>
#include <cstring>
#define maxn 100005
using namespace std;
int b[maxn];  // b存放每个店的订单数量
int book[maxn];  //判断外卖店的优先级在过程中有没有大于过5,如果有,赋值为1
int book2[maxn];  //判断某个时刻哪个外卖店有订单,如果有,则为1
struct waimai {
    int ts;
    int id;
} a[maxn];
bool cmp(waimai a, waimai b) {
    return a.ts < b.ts;
}
int main() {
    int n, m, t;
    int count = 0;
    scanf("%d%d%d", &n, &m, &t);
    for (int i = 1; i <= m; i++) {
        scanf("%d%d", &a[i].ts, &a[i].id);
    }
    sort(a + 1, a + m + 1, cmp);
    for (int i = 1; i <= t; i++) {
        int x;
        for (int j = 1; j <= m; j++) {
            if (i == a[j].ts) {
                x = a[j].id;
                b[x] += 2;
                if (b[x] > 5)
                    book[x] = 1;
                book2[x] = 1;
            }
        }
        for (int l = 1; l <= n; l++) {
            if (b[l] > 0 && book2[l] == 0)
                b[l]--;
            if (b[l] <= 3)
                book[l] = 0;
        }
        memset(book2, 0, sizeof(book2));
    }
    for (int i = 1; i <= n; ++i) {
        if (book[i] == 1)
            count++;
    }
    printf("%d\n", count);
    return 0;
}

题解:枚举时刻t,用book记录是否在优先缓存内;book2记录时刻t是否有订单,当t更新的时候book2也要更新

**

试题 H: 修改数组

**
时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分
【问题描述】
给定一个长度为 N 的数组 A = [A1, A2, · · · AN],数组中有可能有重复出现
的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改
A2, A3, · · · , AN。
当修改 Ai 时,小明会检查 Ai 是否在 A1 ∼ Ai−1 中出现过。如果出现过,则
小明会给 Ai 加上 1 ;如果新的 Ai 仍在之前出现过,小明会持续给 Ai 加 1 ,直
到 Ai 没有在 A1 ∼ Ai−1 中出现过。
当 AN 也经过上述修改之后,显然 A 数组中就没有重复的整数了。
现在给定初始的 A 数组,请你计算出最终的 A 数组。
【输入格式】
第一行包含一个整数 N。
第二行包含 N 个整数 A1, A2, · · · , AN 。
【输出格式】
输出 N 个整数,依次是最终的 A1, A2, · · · , AN。
【样例输入】
5
2 1 1 3 4
【样例输出】
2 1 3 4 5
【评测用例规模与约定】
对于 80% 的评测用例,1 ≤ N ≤ 10000。
对于所有评测用例,1 ≤ N ≤ 100000,1 ≤ Ai ≤ 1000000。

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

inline int read() {
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    return x * f;
}

int n, a[M];
int p[M];
int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) {
        a[i] = read();
        while (p[a[i]] != 0) {
            p[a[i]]++;				//记录坑位被查了多少次
            a[i] += p[a[i]] - 1;	//跳过这p[a[i]]-1次的无意义查询
        }
        p[a[i]] = 1;
    }
    for (int i = 0; i < n; ++i) {
        cout << a[i] << " ";
    }
    return 0;
}

题解:一开始暴力写了题解,最后tle了最后两个测试点,然后想着怎么剪枝;首先了解一个事实,假定测试样例为a[] = {1,1,1,1,1},第一次查询后结果为a[] = {1,1,1,1,1},第二次查询后结果为a[] = {1,2,1,1,1},第三次a[] = {1,2,3,1,1},第四次a[] = {1,2,3,4,1},第五次a[] = {1,2,3,4,5},我们发现查询第五次的时候还得从1开始比较然后一次比较2,3,4直至到我,但是我们之前在第二,三,四次比较的时候已经知道这些坑位已经用过了,所以我们可以剪去这些不必要的比较而直接从5号坑位开始比较,于是用p[1]记录下1被查过多少次(k),然后下一个如果还是查询1号位的话可以直接跳过这k个没有意义的查询。

**

试题 I: 糖果

**
时间限制: 1.0s 内存限制: 256.0MB 本题总分:25 分
【问题描述】
糖果店的老板一共有 M 种口味的糖果出售。为了方便描述,我们将 M 种
口味编号 1 ∼ M。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而
是 K 颗一包整包出售。
幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知
道每包内的糖果口味。
给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖
果。
【输入格式】
第一行包含三个整数 N、M 和 K。
接下来 N 行每行 K 这整数 T1, T2, · · · , TK,代表一包糖果的口味。
【输出格式】
一个整数表示答案。如果小明无法品尝所有口味,输出 −1。
【样例输入】
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
试题I: 糖果 14
第十届蓝桥杯大赛软件类省赛 C/C++ 大学 A 组
【样例输出】
2
【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ N ≤ 20 。
对于所有评测样例,1 ≤ N ≤ 100,1 ≤ M ≤ 20,1 ≤ K ≤ 20,1 ≤ Ti ≤ M。

#include <bits/stdc++.h>
#define For(i, a, b) for (register int i = (a); i <= (b); ++i)
#define Rep(i, a, b) for (register int i = (a); i >= (b); --i)
#define Mst(a, b) memset(a, (b), sizeof(a))
using namespace std;
int dp[1 << 10];  //二进制状态压缩
int a[105];
int main() {
    int m, n, k;
    while (scanf("%d%d%d", &n, &m, &k) != EOF) {
        set<int> v;  //记录n包中所有糖果的种类数
        memset(dp, 0x3f, sizeof(int) * (1 << m));  //初始化
        dp[0] = 0;
        For(i, 1, n) {
            int x = 0, y;
            For(j, 1, k) {
                scanf("%d", &y);
                --y;
                v.insert(y);
                x |= (1 << y);
            }
            a[i] = x;  // x的二进制形式第i位为1代表这个集合中有第i+1种糖果
            dp[x] = 1;  //这种状态只需要第i包糖果1包就可以达到
        }
        if ((int)v.size() < m)  //把n包唐都买了也吃不到m中糖果
        {
            printf("-1\n");
            continue;
        }
        int ed = (1 << m) - 1;  //目标状态
        For(i, 1, n) {
            For(state, 0, ed) {
                if (dp[state] > 200)  //这里>200就是没有能到达这个state状态的组合包
                    continue;  //用已有的状态去更新加上第i包糖果后的状态
                int to = a[i] | state;
                dp[to] = min(dp[to], 1 + dp[state]);
            }
        }
        printf("%d\n", dp[ed]);
    }

    return 0;
}

题解:用状压dp,算是模板题了

**

试题 J: 组合数问题

**
时间限制: 1.0s 内存限制: 256.0MB 本题总分:25 分
【问题描述】
给n,m,k,求有多少对( i , j )满足1 ≤ i ≤ n,0 ≤ j ≤ min(i,m)且C ( i , j ) ≡ 0 ( m o d k ),k 是质数。其中 C ( i , j ) 是组合数,表示从 i 个不同的数中选出j个组成 一个集合的方案数。
第一行两个数 t, k,其中 t 代表该测试点包含 t 组询问,k 的意思与上文中相同。
接下来 t 行每行两个整数 n, m,表示一组询问。
【输出格式】
输出 t 行,每行一个整数表示对应的答案。由于答案可能很大,请输出答案除以 109 + 7 的余数。
【样例输入】
1 2
3 3
【样例输出】
1
【样例说明】
在所有可能的情况中,只有 C(1,2) = 2 是 2 的倍数。
【样例输入】
2 5
4 5
6 7
【样例输出】
0
7
【样例输入】
3 23
23333333 23333333
233333333 233333333
2333333333 2333333333
【样例输出】
851883128
959557926
680723120
【数据规模和约定】
对于所有评测用例,1 ≤ k ≤ 108, 1 ≤ t ≤ 105, 1 ≤ n, m ≤ 1018,且 k 是质数。
评测时将使用 10 个评测用例测试你的程序,每个评测用例的限制如下:
评测用例编号 t n, m k
1, 2 ≤ 1 ≤ 2000 ≤ 100
3, 4 ≤ 105 ≤ 2000 ≤ 100
5, 6, 7 ≤ 100 ≤ 1018 ≤ 100
8, 9, 10 ≤ 105 ≤ 1018 ≤ 108

//解法一:杨辉三角+二维前缀和
#include<bits/stdc++.h>
using namespace std;
#define modk(x) (((x)>=k)?((x)-k):(x))
const int maxn = 2005;
int c[maxn][maxn], n, m, k, T;
void init(){
    ///预处理C(i,j)
    c[0][0]=1;
    for(int i=1;i<maxn;i++){//杨辉三角求C(n,m)
        c[i][0]=1%k;
        for(int j=1;j<=i;j++){
            c[i][j]=(c[i-1][j]+c[i-1][j-1])modk;
        }
    }
    ///处理C(i,j)是否为k 的倍数
    for(int i=0;i<maxn;i++){
        for(int j=0;j<=i;j++){
            if(c[i][j]==0) c[i][j]=1;	//如果符合题目要求则刚刚好整除,标记为1方便后序做前缀和
            else c[i][j]=0;
        }
    }
    ///将二维数组C处理成区域前缀和
    for(int i=1;i<maxn;i++){
        int s=0;
        for(int j=0;j<maxn;j++){
            s+=c[i][j];
            c[i][j]=c[i-1][j]+s;
        }
    }
}
int main(){
    scanf("%d%d",&T,&k);
    init();
    while(T--){
        scanf("%d%d",&n,&m);
        printf("%d\n",c[n][m]);
    }
    return 0;
}

//解法二:卢卡斯定理+数位dp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i <  (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define _srep(n,m,i)for (register int i = (n); i >= (m); i--)
#define _sfor(n,m,i)for (register int i = (n); i >  (m); i--)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
const LL Mod = 1e9+7;
const LL inv_2 = 5e8+4;
LL dp[65][4];
/*
用卢卡斯定理后转化为n,m的k进制数中,n中至少有一位大于m则模k为0.
考虑对立事件,求出n中所有位都大于m的对数,在用总方案减去它 
dp[i][0] :第i位无限制 
dp[i][1] :n的前i位为上界,m未达上界 
dp[i][2] :m的前i位位上界,n未达上界 
dp[i][3] :n,m都到达了上界 
*/
LL calc(LL x, LL y) {//x取值[0, x], y取值[0,y] x >=y的数量 
  if(x < 0|| y < 0) return 0;
  if(x < y) {
    x %= Mod; y %= Mod;
    return (x+2) * (x + 1) % Mod * inv_2 % Mod;
  }
  x %= Mod; y %= Mod;
  return ((y + 2) * (y + 1) % Mod * inv_2 % Mod + (x-y) * (y+1) % Mod) % Mod; 
}
LL calc1(LL x, LL y) { // x不变,y取值为[0,y] x >= y的数量 
  return min(x, y) + 1;
}
LL calc2(LL x, LL y) { // y不变 x取值为[0,x] x >= y的数量 
  if(x < y) return 0;
  return x - y + 1;
}
LL a[100], b[100], P;
int main() {
  int t; read(t); read(P);
  while(t--) {
    LL n, m, ans;
    read(n); read(m);
    if(m > n) m = n;
    ans = calc(n, m);    
    int lenn = 0, lenm = 0;
    while(n) a[lenn++] = n % P, n /= P;
    while(m) b[lenm++] = m % P, m /= P;
    n = max(lenn, lenm);
    memset(dp, 0, sizeof dp);
    dp[n][3] = 1;
    for(int i = n-1; ~i; i--) {
      dp[i][0] = dp[i+1][0] * calc(P-1, P-1) + dp[i+1][1] * calc(a[i]-1, P-1)
       + dp[i+1][2] * calc(P-1, b[i]-1) + dp[i+1][3] * calc(a[i]-1, b[i]-1);
      dp[i][1] = dp[i+1][1] * calc1(a[i], P-1) + dp[i+1][3] * calc1(a[i], b[i]-1);
      dp[i][2] = dp[i+1][2] * calc2(P-1, b[i]) + dp[i+1][3] * calc2(a[i]-1, b[i]);
      dp[i][3] = dp[i+1][3] & (a[i] >= b[i]);
      dp[i][0] %= Mod; dp[i][1] %= Mod; dp[i][2] %= Mod;
      a[i] = b[i] = 0;
    }
    ans -= dp[0][0] + dp[0][1] + dp[0][2] + dp[0][3];
    ans %= Mod;
    if(ans < 0) ans += Mod;
    printf("%lld\n", ans);
  }
}
/*
1 2
509295295705797874 372612087921270267
265498746
*/

题解:解法一就是用杨辉三角+二维前缀和,解法二就是直接上卢卡斯定理+数位dp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值