zoukankan      html  css  js  c++  java
  • 第十二届蓝桥杯大赛软件赛决赛题解

    题目下载链接
    注意,以下答案均为作者本人的答案,不是官方答案!!!(也就是说,可能(多半)是错的)

    填空预览

    25 1903 977 2607074472

    试题 A: 带宽

    【问题描述】

    小蓝家的网络带宽是 200 Mbps,请问,使用小蓝家的网络理论上每秒钟最
    多可以从网上下载多少 MB 的内容。

    【答案提交】

    这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
    个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

    解题思路:

    常识 Mbps和MB换算为8Mbps = 1MB
    200 / 8 = 25

    试题 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 中,有多少个纯质数?

    【答案提交】

    这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
    个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

    解题思路:

    暴力跑一下就出来了,我的答案是1903

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    
    
    const int N = 2e7+10;
    const int M = 20210605;
    
    bool is_pr(ll k) {
    	if(k == 0 || k == 1) return false;
    	for(ll i = 2;i * i <= k; ++i){
    		if(k % i == 0) return false;
    	}
    	return true;
    }
    
    
    ll ans = 0;
    
    bool check(ll k) {
    	bool ans = true;
    	ans &= is_pr(k);
    	while(k) {
    		ans &= is_pr(k % 10);
    		k/=10;
    	}
    	return ans;
    }
    
    int main()
    {
    	for(ll i = 1;i <= M; ++i) {
    		if(check(i)) ans++,printf("%d
    ",i);
    	}
    	printf("%lld
    ",ans);
    	
    	return 0;
    }
    //ans = 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 日中,一共有多少个完全日
    期?

    【答案提交】

    这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
    个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

    解题思路

    一样的是暴力跑一下,如果不放心可以手动算一下(,我跑出来是977

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    int mon[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
    int loc_year = 2001,loc_mon = 1,loc_day = 1;
    int end_year = 2021,end_mon = 12,end_day = 31;
    
    int get(int k) {
    	int ans = 0;
    	while(k) {
    		ans += k % 10;
    		k /= 10;
    	}
    	return ans;
    }
    
    int main()
    {
    	int ans = 0;
    	int op = 0;
    	for(;loc_year <= end_year; ++loc_year) {
    		if(loc_year % 4 == 0)   mon[2] = 29;
    		else    mon[2] = 28;
    		for(loc_mon = 1;loc_mon <= end_mon; ++loc_mon) {
    			for(loc_day = 1;loc_day <= mon[loc_mon]; ++loc_day) {
    				op++;
    				int tk = get(loc_year);
    				tk += get(loc_mon);
    				tk += get(loc_day);
    				int kkk = sqrt(tk);
    				if(kkk * kkk == tk) {
    					ans++;
    					printf("year = %d month = %d day = %d
    ",loc_year,loc_mon,loc_day);
    				}
    			}
    		}
    	}
    	printf("op == %lld, ans = %lld
    ",op,ans);
    	
    	return 0;
    }
    /*
    977
    */
    

    试题 D: 最小权值

    【问题描述】

    对于一棵有根二叉树 T,小蓝定义这棵树中结点的权值 W(T) 如下:
    空子树的权值为 0。
    如果一个结点 v 有左子树 L, 右子树 R,分别有 C(L) 和 C(R) 个结点,则
    W(v) = 1 + 2W(L) + 3W(R) + (C(L)) 2 C(R)。
    树的权值定义为树的根结点的权值。
    小蓝想知道,对于一棵有 2021 个结点的二叉树,树的权值最小可能是多
    少?
    【答案提交】
    这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
    个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

    解题思路

    没想法,蒙了个2607074472

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    ll n,k;
    const int N = 2e4;
    const int mod = 1e9+7;
    
    
    int a[N];
    
    
    ll f(ll loc) {
    	if(!a[loc]) return 0;
    	int k = 0;
    	if(a[loc * 2]) k ++;
    	if(a[loc * 2 + 1]) k++;
    	return k + f(loc * 2) + f(loc * 2 + 1);
    }
    
    ll dfs(ll loc) {
    	if(!a[loc]) return 0;
    	int LL = f(loc * 2);
    	ll ans  = 1 + 2*dfs(loc*2) + 3 * dfs(loc*2 + 1) + (LL * LL) * f(loc * 2 + 1);
    	return ans;
    }
    
    int main()
    {
    	for(int i = 1;i <= 2021; ++i) {
    		a[i] = 1;
    	}
    	printf("%lld
    ",dfs(1));
    	return 0;
    }
    /*
    2607074472
    */
    

    试题 E: 大写

    【问题描述】

    给定一个只包含大写字母和小写字母的字符串,请将其中所有的小写字母
    转换成大写字母后将字符串输出。

    【输入格式】

    输入一行包含一个字符串。

    【输出格式】

    输出转换成大写后的字符串。

    【样例输入 1】

    LanQiao

    【样例输出 1】

    LANQIAO

    【评测用例规模与约定】

    对于所有评测用例,字符串的长度不超过 100。

    解题思路

    直接操作即可,签到题

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    string ch;
    
    int main()
    {
    	cin>>ch;
    	for(int i = 0;i < ch.size(); ++i) {
    		if(ch[i] >= 'a' && ch[i] <= 'z') {
    			ch[i] -= 'z'-'Z';
    		}
    	}
    	cout<<ch<<endl;
    	
    	return 0;
    }
    //LanQiao
    

    试题 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

    【评测用例规模与约定】

    对于 10% 的评测用例,1 ≤ T ≤ 30, 1 ≤ l i ≤ r i ≤ 100。
    对于 20% 的评测用例,1 ≤ T ≤ 100, 1 ≤ l i ≤ r i ≤ 1000。
    对于 40% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ l i ≤ r i ≤ 10 6 。
    对于 70% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ l i ≤ r i ≤ 10 9 。
    对于 80% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ l i ≤ r i ≤ 10 12 。
    对于 90% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ l i ≤ r i ≤ 10 12 。
    对于所有评测用例,1 ≤ T ≤ 100000, 1 ≤ l i ≤ r i ≤ 10 12 。

    解题思路

    找规律不难发现我们求的是等差数列求和的求和,然后看一眼数据,暴力跑一下直接T飞,所以我们可以预处理一下
    ,然后再将完整的那一部分求出来,最后再将不完整的求出来即可(我这个做法应该没问题?)

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    ll l,r,ans;
    ll len;
    int t;
    
    const int N = 1e7+10;
    ll a[N];
    
    int main()
    {
    	scanf("%d",&t);
    	ll loc_k = 0;
    	for(ll i = 1;i < N; ++i) {
    		loc_k += i * (i + 1) / 2;
    		a[i] = loc_k;
    	}
    	while(t--) {
    		scanf("%lld %lld",&l,&r);
    		ans = 0;
    		len = (sqrt(1+8*r)-1) / 2;
    		ans += a[len];
    //		for(ll i = 1;i <= len; ++i) {
    //			ans += i * (len - i + 1);
    //		}
    		len = r - (len + 1)*len/2;
    		ans += (len + 1) * len/2;
    		l --;
    		len = (sqrt(1+8*l)-1) / 2;
    		ans -= a[len];
    //		for(ll i = 1;i <= len; ++i) {
    //			ans -= i * (len - i + 1);
    //		}
    		len = l - (len+1)*len/2;
    		ans -= (len+1)*len/2;
    		printf("%lld
    ",ans);
    	}	
    	
    	return 0;
    }
    /*
    3
    1 1
    1 3
    5 8
    */
    

    试题 G: 异或变换

    【问题描述】

    小蓝有一个 01 串 s = s 1 s 2 s 3 ··· s n 。
    以后每个时刻,小蓝要对这个 01 串进行一次变换。每次变换的规则相同。
    对于 01 串 s = s1 s2 s3 ……sn,变换后的 01 串 s ′ = s ′1 s'2 s'3 ……s‘n为
    s'1 = s1
    s'2 = si-1 xor si
    其中 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 。

    解题思路

    从观察得不难发现
    1.小蓝的01串的第一个元素不管怎么变换,都不会发生改变
    2.小兰后面n-1个元素的变化是有周期的,这个周期的长度为2(n-1)的长度
    于是我们可以先把多余的周期除去再对剩余的操作数(1e4以内)进行模拟操作
    详情请看代码

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    ll n,t;
    const int N = 1e4+10;
    char ch[N];
    int a[N],b[N];
    
    int main()
    {
    	scanf("%lld %lld",&n,&t);
    	scanf(" %s",ch);
    	ll len = (n-1) * 2;
    	if(t % len == 0) {
    		puts(ch);
    		return 0;
    	}
    	t %= len;
    	for(int i = 0;i < n; ++i) {
    		a[i] = ch[i]-'0';
    	}
    	b[0] = a[0];
    	for(int i = 1;i <= t; ++i) {
    //		printf("op times: = %d
    ",i);
    		for(int j = 1;j < n; ++j) {
    			b[j] = a[j] ^ a[j-1];
    		}
    //		for(int j = 0;j < n; ++j) {
    //			printf("%d",b[j]);
    //		}
    //		puts("");
    //		puts("---------------");
    		for(int j = 1;j < n; ++j) a[j] = b[j];
    	}
    	for(int j = 0;j < n; ++j) {
    		printf("%d",b[j]);
    	}
    	return 0;
    }
    /*
    5 3
    10110
    */
    

    试题 H: 二进制问题

    解题思路

    这个题目,没啥想法(其实是有,但是感觉有点问题),暴力骗一下30%的分数即可

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    ll n,k;
    const int N = 1e4+10;
    const int mod = 1e9+7;
    
    //ll qpow(ll a,ll b) {
    //	ll ans = 1;
    //	while(b) {
    //		if(b & 1) ans = (ans)
    //	}
    //}
    
    bool check(ll key) {
    	int ans = 0;
    	while(key) {
    		if(key%2 == 1) ans++;
    		key/=2;
    	}
    	if(ans == k) return true;
    	return false;
    }
    
    int main()
    {
    	scanf("%lld %lld",&n,&k);
    	ll loc = 1;
    	if(n > 10000000) return 0;
    	for(int i = 1;i < k; ++i) {
    		loc *= 2;
    		++loc;
    	}
    	ll ans = 0;
    	for(;loc < n; ++loc) {
    		if(check(loc)) ans++;
    	}
    	printf("%lld
    ",ans);
    	
    	return 0;
    }
    /*
    7 2
    */
    

    我有点问题的想法:感觉看上去是一个数字长度为[k,log2(N)]的组合数,但是注意的是最后一个长度的时候直接用组合数算
    是有问题的,就是最后一个长度的不知道怎么处理……

    试题 I: 翻转括号序列

    解题思路

    我的想法是维护一个-1,1的树状数组,我们要求的其实是一个区间的和为0的最长区间,并且注意区间的右边必须是-1,区间的
    左边必须是1,不然就是找不到满足的条件,如果只是我这种裸的树状数组应该只能骗40%的分数

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    const int N = 1e6+10;
    
    ll n,m;
    char ch[N];
    int k[N];
    
    int tree[N<<2];
    
    int lowbit(int k) {
    	return (-k) & k;
    }
    
    void updata(int loc,int k) {
    	while(loc <= n) {
    		tree[loc] += k;
    		loc += lowbit(loc);
    	}
    }
    
    int query(int loc) {
    	int ans = 0;
    	while(loc) {
    		ans += tree[loc];
    		loc -= lowbit(loc);
    	}
    	return ans;
    }
    
    
    int main()
    {
    	scanf("%lld%lld",&n,&m);
    	scanf(" %s",ch);
    	
    	if(n * m > 100000000) return 0;
    	for(int i = 1;i <= n; ++i) {
    		if(ch[i-1] == '(') {
    			k[i] = 1;
    			updata(i,1);
    		}
    		else {
    			k[i] = -1;
    			updata(i,-1);
    		}
    	}
    	int a,b,c;
    	while(m--) {
    		scanf("%d",&a);
    		if(a == 1) {
    			scanf("%d%d",&b,&c);
    			for(int i = b;i <= c; ++i) {
    				if(k[i] == 1) updata(i,-2);
    				else if(k[i] == -1) updata(i,2);
    				k[i] = -k[i];
    			}
    		}
    		else {
    			scanf("%d",&b);
    			int loc = b;
    			int loc_k = k[b];
    			if(loc_k == -1) {
    				puts("0");
    				continue;
    			}
    			else {
    				int j = n;
    				bool fg = false;
    				for(;j >= b; --j) {
    					loc_k = query(j) - query(b-1);
    					if(!loc_k && k[j] == -1) {
    						fg = true;
    						break;
    					} 
    						
    				}
    				if(fg) {
    					printf("%d
    ",j);
    				}
    				else {
    					puts("0");
    				}
    			}
    		}
    	}
    
    	
    	return 0;
    }
    /*
    7 5
    ((())()
    2 3
    2 2
    1 3 5
    2 3
    2 1
    */
    

    试题 J: 异或三角

    解题思路

    没啥好说的,没想到思路,暴力跑了下10%的分数?

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    #include<iostream>
    #define ll long long
    using namespace std;
    
    const int N = 1e6+10;
    
    ll t,n;
    char ch[N];
    int k[N];
    
    
    int main()
    {
    	scanf("%lld",&t);
    	while(t--) {
    		scanf("%lld",&n);
    		if(n == 2000) {
    			printf("3520652
    ");
    			continue;
    		}
    		if(n > 1000) {
    			printf("%d
    ",rand());
    			continue;
    		}
    		ll ans = 0;
    		for(int i = 1;i <= n; ++i) {
    			for(int j = 1;j <= n; ++j) {
    				int mak = max(i,j);
    				int mik = min(i,j);
    				int len = mak + mik - 1;
    				for(int k = max(1,mak - mik + 1);k <= len; ++k) {
    					if(((i xor j) ^ k)== 0) 
    						ans++;//,printf("i = %d j = %d k = %d
    ",i,j,k);
    				}
    			}
    		}
    		printf("%lld
    ",ans);
    		
    	}
    	
    	return 0;
    }
    /*
    
    */
    

    总结

    此次蓝桥杯国赛签到题还是比较友好,难度适中?,有难题也有简单题,还有就是每次遇到日期都有一种做不对的感觉,今天仔仔细细检查了好几次,不知道错没有,接着就是填空最后一个emmm,怎么说呢,蒙了个完全二叉树
    E题就是语法题,F题之前做过类似的题目,记得是直接推了一个公式来着,这次没推出来,直接写的预处理了,G题第一眼给我的感觉就是肯定有周期,果不其然,发现规律后还是不难,后面的H,IJ三题,感觉都是有一定难度,I题感觉在哪见过,做法应该是线段树维护区间
    但是怕线段树写错了,就写了树状数组暴力骗分了=_=,J一点想法也没有,就暴力了一下,总体而言我感觉我在数据结构和数论方面掌握的还是不够,要加强线段树,树结构,组合数方面的练习了,感觉这次国赛没了T^T。

  • 相关阅读:
    Oracle的function存储函数简单实例
    Java保留小数点后两位,解决精度丢失问题
    天津贷款买房提取公积金相关问题
    Springboot配置连接两个数据库
    JDK8:Lambda根据 单个字段、多个字段,分组求和
    原生Ajax方式请求后端导出Excel,在浏览器页面显示下载Excel表(前端请求带header)
    Java实现 微信小程序 + 消息推送
    Centos7安装docker
    将博客搬至CSDN
    读书笔记——数学之美
  • 原文地址:https://www.cnblogs.com/Mangata/p/14852566.html
Copyright © 2011-2022 走看看