第十二届蓝桥杯决赛 大学 B 组 C/C++ 做题记录
昨天才从外地返校,早上赶去做了核酸买了食物进考场,很困,不过比赛过程还是比较放松的,除了 C 题手残把模拟写错了,其他的算发挥正常吧。
A: 带宽
【问题描述】
小蓝家的网络带宽是 \(200 Mbps\),请问,使用小蓝家的网络理论上每秒钟最多可以从网上下载多少 \(MB\) 的内容。
【我的解答】
\(200Mbps\div 8=25MB/s\)
B: 纯质数
【问题描述】
如果一个正整数只有 1 和它本身两个约数,则称为一个质数(又称素数)。
前几个质数是:\(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, · · · 。\)
如果一个质数的所有十进制数位都是质数,我们称它为纯质数。例如:\(2, 3, 5, 7, 23, 37\) 都是纯质数,
而 \(11, 13, 17, 19, 29, 31\) 不是纯质数。当然 \(1, 4, 35\) 也不是纯质数。
请问,在 \(1\) 到 \(20210605\) 中,有多少个纯质数?
【我的解答】
按照题意,先判断当前数知否为质数再逐位拆分看是否为质数,模拟即可。
#include <bits/stdc++.h>
using namespace std;
int p[]={0,1,4,6,8,9};
inline bool check(int x) {
for (int i=2;i<=sqrt(x);i++)
if (!(x%i)) return false;
while (x) {
for (int i=0;i<6;i++)
if ((x%10)==p[i]) return false;
x/=10;
}
return true;
}
int main() {
int cnt=0;
for (int i=1;i<=20210605;i++)
if (check(i)) cnt++;
cout<<cnt;
return 0;
}
答案:\(1903\)
C: 完全日期
【问题描述】
如果一个日期中年月日的各位数字之和是完全平方数,则称为一个完全日期。
例如:\(2021\) 年 \(6\) 月 \(5\) 日的各位数字之和为 \(2 + 0 + 2 + 1 + 6 + 5 = 16\),而 \(16\) 是一个完全平方数,它是 4 的平方。所以 2021 年 6 月 5 日是一个完全日期。
例如:\(2021\) 年 \(6\) 月 \(23\) 日的各位数字之和为 \(2 + 0 + 2 + 1 + 6 + 2 + 3 = 16\),是一个完全平方数。所以 \(2021\) 年 \(6\) 月 \(23\) 日也是一个完全日期。
请问,从 \(2001\) 年 \(1\) 月 \(1\) 日到 \(2021\) 年 \(12\) 月 \(31\) 日中,一共有多少个完全日期?
【我的解答】
这题写挂了是真的好生气啊,白送的 \(10\) 分无了。
挂的原因是开记录该月有几天的数组下标是从 \(0\) 开始的,但是我再遍历写的从 \(1\) 开始的,这样就把日期算少了。
懒得改了,上一个 \(zkx\) 的代码,答案 \(977\)。
#include<bits/stdc++.h>
using namespace std;
int rn[5]= {29,28,28,28};
int rz[15]= {0,31,0,31,30,31,30,31,31,30,31,30,31};
bool pd(int x) {
int sx=sqrt(x);
return sx*sx==x;
}
int main() {
int ans=0;
for(int y=2001,ty; y<=2021; y++) {
for(int m=1,tm; m<=12; m++) {
for(int d=1,td,cnt; d<=(rz[m]?rz[m]:rn[y%4]); d++) {
ty=y,tm=m,td=d,cnt=0;
for(; ty; ty/=10)cnt+=ty%10;
for(; tm; tm/=10)cnt+=tm%10;
for(; td; td/=10)cnt+=td%10;
if(pd(cnt))ans++;
}
}
}
cout<<ans<<endl;
return 0;
}
D: 最小权值
【问题描述】
对于一棵有根二叉树 \(T\),小蓝定义这棵树中结点的权值 \(W(T)\) 如下:空子树的权值为 \(0\)。
如果一个结点 \(v\) 有左子树 \(L\), 右子树 \(R\),分别有 \(C(L)\) 和 \(C(R)\) 个结点,则 \(W(v) = 1 + 2W(L) + 3W(R) + {(C(L))}^2C(R)\)。
树的权值定义为树的根结点的权值。
小蓝想知道,对于一棵有 \(2021\) 个结点的二叉树,树的权值最小可能是多少?
【我的解答】
\(O(n^2)\) 复杂度的树形 \(DP\),\(dp(x)\) 函数解决包括根节点在内共 \(x\) 节点的最小权值 ,\(f[]\) 数组进行记忆化处理
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
ll ans,f[3007];
ll dp(ll x) {
if (f[x]!=-1) return f[x];
ll minn=min(2*dp(x-1),3*dp(x-1))+1;
for (ll i=1;i<x-1;i++)
minn=min(minn,1+2*dp(i)+3*dp(x-1-i)+i*i*(x-1-i));
return f[x]=minn;
}
int main() {
memset(f,-1,sizeof(f));
f[0]=0,cout<<dp(2021);
return 0;
}
答案:\(2653631372\)
E: 大写
【问题描述】
给定一个只包含大写字母和小写字母的字符串,请将其中所有的小写字母转换成大写字母后将字符串输出。
【我的解答】
模拟。
#include <bits/stdc++.h>
using namespace std;
string in;
int main() {
cin>>in;
for (int i=0;i<(int)in.size();i++)
if (in[i]>='a' && in[i]<='z')
in[i]='A'+in[i]-'a';
cout<<in;
return 0;
}
F: 123
【问题描述】
小蓝发现了一个有趣的数列,这个数列的前几项如下:\(1, 1, 2, 1, 2, 3, 1, 2, 3, 4, ...\)
小蓝发现,这个数列前 \(1\) 项是整数 \(1\),接下来 2 项是整数 \(1\) 至 \(2\),接下来 \(3\) 项是整数 \(1\) 至 \(3\),接下来 \(4\) 项是整数 \(1\) 至 \(4\),依次类推。
小蓝想知道,这个数列中,连续一段的和是多少。
【输入格式】
输入的第一行包含一个整数 \(T\),表示询问的个数。
接下来 \(T\) 行,每行包含一组询问,其中第 \(i\) 行包含两个整数 \(l_i\) 和 \(r_i\),表示
询问数列中第 \(l_i\) 个数到第 \(r_i\) 个数的和。
【输出格式】
输出 \(T\) 行,每行包含一个整数表示对应询问的答案。
【样例输入】
3
1 1
1 3
5 8
【样例输出】
1
4
8
【我的解答】
一道比较显然的数学题,但在考场上还是写写画画了不少时间,需要 \(O(T)\) 的时间复杂度才可以通过所有的数据。
区间内数字的和可以转化成两个端点的前缀和相减的问题,即 \(sum(l,r)=sum(1,r)-sum(1,l-1)\)
我的做法是把每个 \(1,2,3..i\) 都视一组,这样第 \(i\) 组就有 \(1~i\) 共 \(i\) 个数,我们考虑查询下标为 \([1,x]\) 的数列和,
首先需要确定 \(x\) 是落在第几个组,通过二元一次方程的万能公式 \(+\) 周边探测可以 \(O(1)\) 找出 \(c*(c+1)/2<x\) 的最大的 \(c\) 解
这样数列和就被我们分割成了两个部分,第一个是前 \(c\) 段的完整求和,第二个是最后那段的部分求和
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll T,l,r;
inline ll readint() {
ll X=0,w=0; char ch=0;
while (!isdigit(ch)) w|=ch=='-',ch=getchar();
while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline ll te(ll x) { return x<0?0:x*(x+1)/2; } // 1,2,3..n 求和
inline ll qe(ll x) { return x*(x+1)*(2*x+1)/6; } // 1^2+2^2+3^2..n^2 求和
ll func(ll x) {
int c=(sqrt(8*x+1)-1)/2;
// 写这么多判断是去除开方和除造成的误差
// 要找到比 x 小的最大的 c*(c+1)/2 的 c
if (te(c+4)<x) c=c+4;
else if (te(c+3)<x) c=c+3;
else if (te(c+2)<x) c=c+2;
else if (te(c+1)<x) c=c+1;
else if (te(c)<x) c=c;
else if (te(c-1)<x) c=c-1;
else if (te(c-2)<x) c=c-2;
else if (te(c-3)<x) c=c-3;
else if (te(c-4)<x) c=c-4;
return (qe(c)+te(c))/2+te(x-te(c));
}
int main() {
T=readint();
while (T--) {
l=readint(),r=readint();
cout<<func(r)-func(l-1)<<'\n';
}
return 0;
}
G: 异或变换
【问题描述】
小蓝有一个 \(01\) 串 \(s = s1 s2 s3 · · · sn\)。
以后每个时刻,小蓝要对这个 \(01\) 串进行一次变换。每次变换的规则相同。
对于 \(01\) 串 \(s = s1 s2 s3 · · · sn\),变换后的 \(01\) 串 \(s'=s_1's_2's_3' · · · s_n'\)
\(s_1'=s1;\)
\(s_i'=s_{i-1}⊕s_i\)
其中 \(a ⊕ b\) 表示两个二进制的异或,当 \(a\) 和 \(b\) 相同时结果为 \(0\),当 \(a\) 和 \(b\) 不同时结果为 \(1\)。
请问,经过 \(t\) 次变换后的 \(01\) 串是什么?
【输入格式】
输入的第一行包含两个整数 \(n, t\),分别表示 \(01\) 串的长度和变换的次数。
第二行包含一个长度为 \(n\) 的 \(01\) 串。
【输出格式】
输出一行包含一个 \(01\) 串,为变换后的串。
【样例输入】
5 3
10110
【样例输出】
11010
【样例说明】
初始时为 \(10110\),变换 \(1\) 次后变为 \(11101\),变换 \(2\) 次后变为 \(10011\),变换 \(3\) 次后变为 \(11010\)。
【评测用例规模与约定】
对于 \(40\%\) 的评测用例,\(1 ≤ n ≤ 100, 1 ≤ t ≤ 1000\)。
对于 \(80\%\) 的评测用例,\(1 ≤ n ≤ 1000, 1 ≤ t ≤ 10^9\)。
对于所有评测用例,\(1 ≤ n ≤ 10000, 1 ≤ t ≤ 10^{18}\)。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
string a;
ll n,t,v[107];
int main() {
v[0]=1;
for (ll i=1;i<=60;i++) v[i]=v[i-1]<<1;
cin>>n>>t;
for (ll i=1;i<=60;i++) {
if (n>v[i-1] && n<=v[i]) { t%=v[i]; break; }
}
if (n==1) t=0; cin>>a;
while (t--) {
for (ll i=n-1;i>=1;i--) {
if (a[i]=='1' && a[i-1]=='0') a[i]='1';
else if (a[i]=='0' && a[i-1]=='1') a[i]='1';
else a[i]='0';
}
}
cout<<a<<endl;
return 0;
}
H: 二进制问题
【问题描述】
小蓝最近在学习二进制。他想知道 \(1\) 到 \(N\) 中有多少个数满足其二进制表示
中恰好有 \(K\) 个 \(1\)。你能帮助他吗?
【输入格式】
输入一行包含两个整数 \(N\) 和 \(K\)。
【输出格式】
输出一个整数表示答案。
【样例输入】
7 2
【样例输出】
3
【评测用例规模与约定】
对于 \(30\%\) 的评测用例,\(1 ≤ N ≤ 106, 1 ≤ K ≤ 10\)。
对于 \(60\%\) 的评测用例,\(1 ≤ N ≤ 2 × 109, 1 ≤ K ≤ 30\)。
对于所有评测用例,\(1 ≤ N ≤ 1018, 1 ≤ K ≤ 50\)。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n,k,ans,v[107],C[107][107];
void init() {
v[0]=1,C[0][0]=C[1][0]=C[1][1]=1;
for (ll i=1;i<=100;i++) v[i]=v[i-1]<<1;
for (ll i=2;i<=100;i++)
for (ll j=0;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
}
void func(ll p,ll r,ll sum) {
if (sum>n) return;
if (!r) { ans++; return; }
for (ll i=p-1;i>=0 && i+1>=r;i--)
func(i,r-1,sum+v[i]);
}
int main() {
init(); cin>>n>>k;
for (ll i=59;i>=0;i--) {
if (v[i]-1==n) { ans+=C[i][k]; break; }
else if (v[i]-1<n) {
ans+=C[i][k];
func(i,k-1,v[i]);
break;
}
}
cout<<ans;
return 0;
}
I: 翻转括号序列
【问题描述】
给定一个长度为 \(n\) 的括号序列,要求支持两种操作:
-
将 \([L_i, R_i]\) 区间内(序列中的第 \(L_i\) 个字符到第 \(R_i\) 个字符)的括号全部翻转(左括号变成右括号,右括号变成左括号)。
-
求出以 \(L_i\) 为左端点时,最长的合法括号序列对应的 \(R_i\) (即找出最大的 \(R_i\) 使 \([Li, Ri]\) 是一个合法括号序列)。
【输入格式】
输入的第一行包含两个整数 \(n, m\),分别表示括号序列长度和操作次数。
第二行包含给定的括号序列,括号序列中只包含左括号和右括号。
接下来 \(m\) 行,每行描述一个操作。如果该行为 \(“1 Li Ri”\),表示第一种操作,
区间为 \([L_i, R_i]\) ;如果该行为 \(“2 L_i”\) 表示第二种操作,左端点为 \(L_i\)。
【输出格式】
对于每个第二种操作,输出一行,表示对应的 \(R_i\)。如果不存在这样的 \(R_i\),请输出 \(0\)。
【样例输入】
7 5
((())()
2 3
2 2
1 3 5
2 3
2 1
【样例输出】
4
7
0
0
【评测用例规模与约定】
对于 \(20\%\) 的评测用例,\(n, m ≤ 5000\);
对于 \(40\%\) 的评测用例,\(n, m ≤ 30000\);
对于 \(60\%\) 的评测用例,\(n, m ≤ 100000\);
对于所有评测用例,\(1 ≤ n ≤ 10^6, 1 ≤ m ≤ 2 × 10^5\)。
#include <bits/stdc++.h>
using namespace std;
int n,m,len;
string s;
int main() {
cin>>n>>m>>s,len=(int)s.size();
while (m--) {
int opt; cin>>opt;
if (opt==1) {
int l,r;
cin>>l>>r,l--,r--;
for (int i=l;i<=r;i++) {
if (s[i]=='(') s[i]=')';
else s[i]='(';
}
}
else {
int l,c=1,ans=0; cin>>l,l--;
if (s[l]==')') { printf("0\n"); continue; }
for (int i=l+1;i<len;i++) {
if (s[i]=='(') c++; else c--;
if (!c) ans=i; else if (c<0) break;
}
if (!ans) printf("0\n");
else printf("%d\n",ans+1);
}
}
return 0;
}
J: 异或三角
【问题描述】
给定 \(T\) 个数 \(n_1, n_2, · · · , n_T\),对每个 ni 请求出有多少组 a, b, c 满足:
\(1. 1 ≤ a, b, c ≤ ni;\)
\(2. a ⊕ b ⊕ c = 0\),其中 ⊕ 表示二进制按位异或;
\(3.\) 长度为 \(a, b, c\) 的三条边能组成一个三角形。
【输入格式】
输入的第一行包含一个整数 \(T\)。
接下来 \(T\) 行每行一个整数,分别表示 \(n_1, n_2, · · · , n_T\)。
【输出格式】
输出 \(T\) 行,每行包含一个整数,表示对应的答案。
【样例输入】
2
6
114514
【样例输出】
6
11223848130
【评测用例规模与约定】
对于 \(10\%\) 的评测用例,\(T = 1, 1 ≤ n_i ≤ 200\);
对于 \(20\%\) 的评测用例,\(T = 1, 1 ≤ n_i ≤ 2000\);
对于 \(50\%\) 的评测用例,\(T = 1, 1 ≤ n_i ≤ 2^{20}\)
对于 \(60\%\) 的评测用例,\(1 ≤ T ≤ 100000, 1 ≤ n_i ≤ 2^{20}\);
对于所有评测用例,\(1 ≤ T ≤ 100000, 1 ≤ n_i ≤ 2^{30}\)。
#include <bits/stdc++.h>
#define MAXN 2007
using namespace std;
int T,n,ans=0;
int main() {
scanf("%d",&T);
for (int i=1;i<=T;i++) scanf("%d",&n);
for (int a=1;a<=n;a++) {
for (int b=1;b<=n;b++) {
int c=0^a^b;
if (c>=1 && c<=n && a+b>c && a+c>b && b+c>a) ans++;
}
}
cout<<ans;
return 0;
}