2020第十一届蓝桥杯软件类省赛第二场C/C++ 大学 B 组(题解)

试题 A: 门牌制作

问题描述
小蓝要为一条街的住户制作门牌号。
这条街一共有 2020 位住户,门牌号从 1 到 2020 编号。
小蓝制作门牌的方法是先制作 0 到 9 这几个数字字符,最后根据需要将字
符粘贴到门牌上,例如门牌 1017 需要依次粘贴字符 1、 0、 1、 7,即需要 1 个
字符 0, 2 个字符 1, 1 个字符 7。
请问要制作所有的 1 到 2020 号门牌,总共需要多少个字符 2?

直接暴力

/*
  Author : lifehappy
*/
#include 

using namespace std;

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int ans = 0;
    for(int i = 1; i <= 2020; i++) {
        int temp = i;
        while(temp) {
            if(temp % 10 == 2) ans++;
            temp /= 10;
        }
    }
    printf("%d\n", ans);
	return 0;
}
/*
624
*/

试题 B: 既约分数

【问题描述】
如果一个分数的分子和分母的最大公约数是 1,这个分数称为既约分数。
例如, 3 4 , 5 2 , 1 8 , 7 1 \frac{3}{4}, \frac{5}{2}, \frac{1}{8}, \frac{7}{1} 43,25,81,17都是既约分数。
请问,有多少个既约分数,分子和分母都是 1 到 2020 之间的整数(包括 1
和 2020)?

显然这不是一道简单题,我们写下数学式子来
∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = 1 ] ∑ k = 1 n μ ( k ) ∑ i = 1 n k ∑ j = 1 n k \sum_{i = 1} ^{n} \sum_{j = 1} ^{n} [gcd(i, j) = 1]\\ \sum_{k = 1} ^{n} \mu(k) \sum_{i = 1} ^{\frac{n}{k}} \sum_{j = 1} ^{\frac{n}{k}}\\ i=1nj=1n[gcd(i,j)=1]k=1nμ(k)i=1knj=1kn
然后就只要线性筛,加数论分块即可。

/*
  Author : lifehappy
*/
#include 

using namespace std;

const int N = 1e4 + 10;

int prime[N], mu[N], cnt;

bool st[N];

void init() {
    mu[1] = 1;
    for(int i = 2; i < N; i++) {
        if(!st[i]) {
            prime[++cnt] = i;
            mu[i] = -1;
        }
        for(int j = 1; j <= cnt && 1ll * i * prime[j] < N; j++) {
            st[i * prime[j]] = 1;
            if(i % prime[j] == 0) {
                break;
            }
            mu[i * prime[j]] = -mu[i];
        }
    }
    for(int i = 1; i < N; i++) {
        mu[i] += mu[i - 1];
    }
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    init();
    int n = 2020, ans = 0;
    for(int l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        ans += (mu[r] - mu[l - 1]) * (n / l) * (n / l);
    }
    printf("%d\n", ans);
	return 0;
}
/*
2481215
*/

试题 C: 蛇形填数

如下图所示,小明用从 1 开始的正整数“蛇形”填充无限大的矩阵。
1 2 6 7 15 :::
3 5 8 14 :::
4 9 13 :::
10 12 :::
11 :::
:::
容易看出矩阵第二行第二列中的数是 5。请你计算矩阵中第 20 行第 20 列
的数是多少 ?

这题也是模拟即可,所以就直接上代码吧,看代码就懂了。

/*
  Author : lifehappy
*/
#include 

using namespace std;

int a[50][50];

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n = 45, num = 45 * 45;
    for(int i = 1, cnt = 1; i <= n && cnt <= num; i++) {
        if(i & 1) {
            for(int x = i, y = 1; x >= 1 && y <= i; x--, y++) {
                a[x][y] = cnt++;
            }
        }
        else {
            for(int x = 1, y = i; x <= i && y >= 1; x++, y--) {
                a[x][y] = cnt++;
            }
        }
    }
    printf("%d\n", a[20][20]);
	return 0;
}
/*
761
*/

试题 D: 跑步锻炼

【问题描述】
小蓝每天都锻炼身体。
正常情况下,小蓝每天跑 1 千米。如果某天是周一或者月初(1 日),为了
激励自己,小蓝要跑 2 千米。如果同时是周一或月初,小蓝也是跑 2 千米。
小蓝跑步已经坚持了很长时间,从 2000 年 1 月 1 日周六(含)到 2020 年
10 月 1 日周四(含)。请问这段时间小蓝总共跑步多少千米?

按照天数去模拟就OK了。

/*
  Author : lifehappy
*/
#include 

using namespace std;

int day[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int a = 2000, b = 1, c = 1, num = 1, ans = 0;
    while(a != 2020 || b != 10 || c != 2) {
        if(c == 1 || num % 7 == 3) ans += 2;
        else ans += 1;
        int nowday = day[b];
        if((a % 400 == 0 || (a % 4 == 0 && a % 100 != 0)) && b == 2) nowday++;
        c++, num++;
        if(c > nowday) {
            c = 1;
            b += 1;
        }
        if(b == 13) {
            a += 1;
            b = 1;
        }
    }
    printf("%d\n", ans);
	return 0;
}
/*
8879
*/

试题 E: 七段码

总共有7根管子,二进制枚举,然后bfs或者dfs判断是否联通即可。

下面用0 -> a, 1 -> b……6 -> g,也就是用0代表a,以此类推,,

/*
  Author : lifehappy
*/
#include 

using namespace std;

bool judge(int pos, int value) {
    queue<int> q;
    q.push(pos);
    value ^= 1ll << pos;
    while(q.size()) {
        int temp = q.front();
        q.pop();
        if(temp == 0) {
            if(value >> 1 & 1) {
                value ^= 1 << 1;
                q.push(1);
            }
            if(value >> 5 & 1) {
                value ^= 1 << 5;
                q.push(5);
            }
        }
        else if(temp == 1) {
            if(value >> 0 & 1) {
                value ^= 1 << 0;
                q.push(0);
            }
            if(value >> 2 & 1) {
                value ^= 1 << 2;
                q.push(2);
            }
            if(value >> 6 & 1) {
                value ^= 1 << 6;
                q.push(6);
            }
        }
        else if(temp == 2) {
            if(value >> 1 & 1) {
                value ^= 1 << 1;
                q.push(1);
            }
            if(value >> 3 & 1) {
                value ^= 1 << 3;
                q.push(3);
            }
            if(value >> 6 & 1) {
                value ^= 1 << 6;
                q.push(6);
            }
        }
        else if(temp == 3) {
            if(value >> 2 & 1) {
                value ^= 1 << 2;
                q.push(2);
            }
            if(value >> 4 & 1) {
                value ^= 1 << 4;
                q.push(4);
            }
        }
        else if(temp == 4) {
            if(value >> 3 & 1) {
                value ^= 1 << 3;
                q.push(3);
            }
            if(value >> 5 & 1) {
                value ^= 1 << 5;
                q.push(5);
            }
            if(value >> 6 & 1) {
                value ^= 1 << 6;
                q.push(6);
            }
        }
        else if(temp == 5) {
            if(value >> 0 & 1) {
                value ^= 1 << 0;
                q.push(0);
            }
            if(value >> 4 & 1) {
                value ^= 1 << 4;
                q.push(4);
            }
            if(value >> 6 & 1) {
                value ^= 1 << 6;
                q.push(6);
            }
        }
        else {
            if(value >> 1 & 1) {
                value ^= 1 << 1;
                q.push(1);
            }
            if(value >> 2 & 1) {
                value ^= 1 << 2;
                q.push(2);
            }
            if(value >> 4 & 1) {
                value ^= 1 << 4;
                q.push(4);
            }
            if(value >> 5 & 1) {
                value ^= 1 << 5;
                q.push(5);
            }
        }
    }
    return value == 0;
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int ans = 0;
    for(int i = 1; i < 1 << 7; i++) {
        int j;
        for(j = 0; j < 7; j++) {
            if(i >> j & 1) {
                break;
            }
        }
        if(judge(j, i)) {
            ans++;
        }
    }
    printf("%d\n", ans);
	return 0;
}
/*
80
*/

试题 F: 成绩统计

统计 ≥ 60 \geq 60 60 ≥ 85 \geq 85 85的数量,然后按照要求四舍五入即可。

/*
  Author : lifehappy
*/
#include 

using namespace std;

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n, a, res = 0, ans = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a);
        if(a >= 60) res++;
        if(a >= 85) ans++;
    }
    printf("%.0f%%\n%.0f%%\n", (double)res * 100.0 / n, (double)ans * 100.0 / n);
	return 0;
}

试题 G: 回文日期

跟D题一样,按照天数模拟,然后再judge就行了。

/*
  Author : lifehappy
*/
#include 

using namespace std;

int day[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

bool judge1(int a, int b, int c) {
    int d = a * 10000 + b * 100 + c;
    int num[10], cnt = 9;
    while(d) {
        num[--cnt] = d % 10;
        d /= 10;
    }
    for(int l = 1, r = 8; l < r; l++, r--) {
        if(num[l] != num[r]) {
            return false;
        }
    }
    return true;
}

bool judge2(int a, int b, int c) {
    int d = a * 10000 + b * 100 + c;
    int num[10], cnt = 9;
    while(d) {
        num[--cnt] = d % 10;
        d /= 10;
    }
    for(int l = 1, r = 8; l < r; l++, r--) {
        if(num[l] != num[r] || num[l] != num[l & 1 ? 1 : 2]) {
            return false;
        }
    }
    return true;
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int a, b, c, d, flag = 0;
    scanf("%d", &d);
    a = d / 10000, b = (d / 100) % 100, c = d % 100;
    while(flag < 2) {
        int nowday = day[b];
        if(((a % 400 == 0) || (a % 4 == 0 && a % 100 != 0)) && b == 2) nowday++;
        c++;
        if(c > nowday) {
            c = 1;
            b += 1;
        }
        if(b == 13) {
            a += 1;
            b = 1;
        }
        if(!flag && judge1(a, b, c)) {
            printf("%d%02d%02d\n", a, b, c);
            flag += 1;
        }
        if(judge2(a, b, c)) {
            printf("%d%02d%02d\n", a, b, c);
            flag += 1;
        }
    }
	return 0;
}

试题 H: 子串分值和

这题稍微有点思维吧,我们定义,对于一个字符串我们只记录这个字母在这个字符中出现的第一次有贡献,

接下来就是统计,以某个字符为第一次出现的字母的字串数量了,

我们定义 l a s t [ i ] last[i] last[i]表示字符 i i i在当前枚举点之前最后一次出现的位置,显然这个字符的左端点就可以落在 [ l a s t [ i ] + 1 , n o w ] [last[i] + 1, now] [last[i]+1,now]

这个区间了,然后右端点可以在 n o w , n now, n now,n区间,所有的字串就是这两个长度的乘积,然后就是非常简短的代码了。

/*
  Author : lifehappy
*/
#include 

using namespace std;

typedef long long ll;

const int N = 1e5 + 10;

int last[30], n;

char str[N];

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    scanf("%s", str + 1);
    n = strlen(str + 1);
    ll ans = 0;
    for(int i = 1; i <= n; i++) {
        ans += 1ll * (i - last[str[i] - 'a']) * (n - i + 1);
        last[str[i] - 'a'] = i;
    }
    printf("%lld\n", ans);
    return 0;
}

试题 I: 平面切分

平面上有 N 条直线,其中第 i 条直线是 y = A i ⋅ x + B i y = A_i · x + B_i y=Aix+Bi
请计算这些直线将平面分成了几个部分。

这题不会,待补,,,

试题 J: 字串排序

【问题描述】
小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。
小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,
则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串 lan 排序,只需要 1 次交换。对于字符串 qiao 排序,
总共需要 4 次交换。
小蓝的幸运数字是 V,他想找到一个只包含小写英文字母的字符串,对这
个串中的字符进行冒泡排序,正好需要 V 次交换。请帮助小蓝找一个这样的字
符串。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,
请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。

思路

显然要使长度最短,我们就不能浪费每一个字母,所以,一定有字母是递减的顺序的,

要使字典序最短,每个字母出现的数量一定是要从前往后递增的,这样就好了,,

限制一下每个字母最多出现的次数然后就是 d f s dfs dfs爆搜,

但是考场上限制的次数太小了,写了 7 7 7,只能测到 91000 91000 91000左右的测试点,改到 8 8 8就好了。

(这题貌似好像有点问题,并不是正解 >_<)

/*
  Author : lifehappy
*/
#include 

using namespace std;

const int N = 1e4 + 10;

char ans[N], res[N];

int n, len;

bool judge() {
	int i = len;
	while(ans[i] == res[i] && i) i--;
	return res[i] < ans[i];
}

void dfs(int now, int maxn, int m, int sum) {
	if(sum == n) {
		if(m < len || (m == len && judge())) {
			len = m;
			for(int i = 1; i <= len; i++) {
				ans[i] = res[i];
			}
		}
		return ;
	}
	if(now >= 26) return ;
	for(int i = 1; i <= maxn; i++) {
		int temp = sum + m * i;
		if(temp > n) return ;
		res[m + i] = char(now + 'a');
		dfs(now + 1, i, m + i, temp);
	}
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    len = 0x3f3f3f3f;
    scanf("%d", &n);
    dfs(0, 8, 0, 0);
    for(int i = len; i >= 1; i--) {
    	putchar(ans[i]);
	}
	puts("");
	return 0;
}

你可能感兴趣的:(2020第十一届蓝桥杯软件类省赛第二场C/C++ 大学 B 组(题解))