2021第十二届蓝桥杯国赛B组题解(C C++)_3>2>8>5为什么输出是0(3)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

输入的第一行包含一个整数 _T _,表示询问的个数。
接下来 _T _行,每行包含一组询问,其中第 _i _行包含两个整数 _li _和 ri,表示询问数列中第 _li _个数到第 _ri _个数的和。

【输出格式】

输出 _T _行,每行包含一个整数表示对应询问的答案。

【样例输入】

3
1 1
1 3
5 8

【样例输出】

1
4
8

【评测用例规模与约定】

对于 10% 的评测用例,1 ≤ _T _≤ 30, 1 ≤ _li _≤ _ri _≤ 100。 对于 20% 的评测用例,1 ≤ _T _≤ 100, 1 ≤ _li _≤ _ri _≤ 1000。对于 40% 的评测用例,1 ≤ _T _≤ 1000, 1 ≤ _li _≤ _ri _≤ 106。对于 70% 的评测用例,1 ≤ _T _≤ 10000, 1 ≤ _li _≤ _ri _≤ 109。对于 80% 的评测用例,1 ≤ _T _≤ 1000, 1 ≤ _li _≤ _ri _≤ 1012。
对于 90% 的评测用例,1 ≤ _T _≤ 10000, 1 ≤ _li _≤ _ri _≤ 1012。对于所有评测用例,1 ≤ _T _≤ 100000, 1 ≤ _li _≤ _ri _≤ 1012。

【解题思路:类比金字塔+前缀和 <= 100%】

将数列看成金字塔,转换成求第na层第ca个数到nb层cb个数中间
1
1 2
1 2 3

1 2 3 4

1 2 3 4 5

思路举例:
例题的5 8 ==> 8
[第3层第2个数] --[第4层第2个数] :(2 + 3)[第三层后面]+ 0[中间层] + (1 + 2)[第四层前面] = 8
再举个好看的例子: 4 15 ==> 21
[第3层第1个数] – [第5层第5个数] : (6)第三层后面 + 10 中间层 + 15 [第四层第五个数前面和] = 21

然后讲上述思路转换为编程思路:

分层解决问题,把内容模块化,然后逐个实现即可

  1. 估计层数 – (1+x)*x/ 2>=1e12 ==> x = 1500000层
  2. 所以1500000层可以容纳1e12个数,也可以用数组存下
  3. 关键公式:sum = 前缀和求ab中间层数的和 + a当前层后面几个数量 + b当前层前面几个数量
  4. 解决给定第n个数,求出他在第几层第几个数。
  5. 解决求中间层数
#include<bits/stdc++.h>
#include<iostream>
#define ll long long
using namespace std;

ll dp[1500000]; // 每一层的和
ll dpSum[1500000]; // 层的前缀和
/\*int dp[30] = {0,1,1,2,1,2,3,1,2,3,4,1,2,3,4,5,1,2,3,4,5,6};
 1 1
 2 1 2
 3 1 2 3
 4 1 2 3 4 
 5 1 2 3 4 5
\*/
 //函数思路是找到a和b是第几层的第几个
 //sum = 前缀和求ab中间层数的和 + a当前层后面几个数量 + b当前层前面几个数量
 ll check(ll a,ll b){
 	ll temp = 1;
 	ll flag = 1;
 	while((temp+1)\*(temp)/2 < a){
 		temp++;
 	}
 	ll aDeep = temp,aCnt = aDeep - (((aDeep+1)\*(aDeep)/2) - a);
 	temp = 1;
 	flag = 1;
 	while((temp+1)\*(temp)/2 < b){
 		temp++;
 	}
 	ll bDeep = temp,bCnt = bDeep - (((bDeep+1)\*(bDeep)/2) - b);
 	ll sum = 0;
 	//cout << aDeep << ' ' << aCnt << ' ' << bDeep << ' ' << bCnt << endl; 
	if(bDeep > aDeep)
	  sum = dpSum[bDeep-1] - dpSum[aDeep];
	if(bDeep > aDeep){
		sum += (aDeep+aCnt)\*(aDeep+1-aCnt)/2;
 		sum += (1+bCnt)\*(bCnt+1-1)/2;
	}else{
		sum += (aCnt + bCnt)\*(bCnt + 1 - aCnt)/2;
	}
 	
 	return sum;
 }
 
int main(){
	dp[0] = 0;
	dpSum[0] = 0;
    //推算到 1500000层正好大约1e12次方个数
    //处理1500000层的数据
	for(int i = 1; i <= 1500000; i++){
		dp[i] = dp[i-1] + i;
		dpSum[i] = dpSum[i-1] + dp[i];
	}
    //输入数据
	ll n;
	cin >> n;
	for(ll i = 0; i < n; i++){
		ll a, b;
		cin >> a >> b;
		cout << check(a,b) << endl;
	}
	return 0;
} 

试题 G: 异或变换[20 分]

时间限制: 1.0s 内存限制: 256.0MB

【问题描述】

小蓝有一个 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 _= si−1 ⊕ 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 _≤ 109。
对于所有评测用例,1 ≤ _n _≤ 10000, 1 ≤ _t _≤ 1018。

【解题思路:set去重找规律 40%+】

map记录第几个是那种样子
set标记,第二次遇上就退出循环

//变异 
#include<bits/stdc++.h>
#include<iostream>
#define ll long long
using namespace std;


map<ll,string> m;
set<string> s; 

int main(){
	ll len,cnt,cntTemp;
	cin >> len >> cnt;
	cntTemp = cnt;
	string str;
	cin >> str;
	ll flag = 0;
	while(cnt--){
		string temp = str;
		for(int i = 1; i < len; i++){
			temp[i] = (str[i-1]-'0')^(str[i]-'0') + '0';
		}
		//cout << temp << endl;
		if(s.count(temp) == 1){
			//cout<< "提前循环"; 
			break;
		}
		s.insert(temp);
		flag += 1;
		m[flag] = temp;
		str = temp;
	}
	if(cnt < 0){
		cout << str;
	}else{
		ll index = cntTemp % flag; 
		if(index == 0) index = flag;
		cout<< m[index];
	}
	return 0;
} 

试题 H: 二进制问题[20 分]

时间限制: 1.0s 内存限制: 256.0MB

【问题描述】

小蓝最近在学习二进制。他想知道 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。

【解题思路:dfs混 <60%的分】

暴力循环遍历可以拿稳30%的
dfs找可以混60%的分
但考试最后没空写完另一种情况了,估计30%左右

#include<bits/stdc++.h>
#include<iostream>
#define ll long long
using namespace std;
// 60位数可以表示1e18 
// dp / 搜索 
int temp[60] = {0},maxNum[60] = {0};
int pos[60] = {0}; //第几个i在哪个位置 
string str;
ll N,K;
ll ans = 1;
int haveOne = 0,Cnt;
//第n个1 

int find(int n){
	for(int i = 0; i < Cnt; i++){
		
	}
}
void dfs(int n){
	if(n < 0) return;
	int nowIndex = pos[n];
	int tIndex = nowIndex + 1;
	
	while(tIndex < Cnt && maxNum[tIndex] == 0){
		swap(maxNum[tIndex],maxNum[nowIndex]);
		ans++;
		dfs(n-1);
		swap(maxNum[tIndex],maxNum[nowIndex]);
		tIndex++;
	}
	dfs(n-1);
}

int main(){
	cin >> N >> K;
	Cnt = 0;
	while(N){
		temp[Cnt++] = N % 2;
		N/=2;
	}
	for(int i = 0; i < Cnt; i++){
		maxNum[i] = temp[Cnt - 1 - i];
		if(maxNum[i] == 1){
			pos[haveOne++] = i;
		}	
		//cout << maxNum[i] << endl;
	}
    //已有的大于需要的就舍去后面的1
	if(haveOne > K){
		int flag =  haveOne - K;
		while(flag--){

			for(int i = Cnt - 1; i >= 0; i--){
				if(maxNum[i] == 1){
					maxNum[i] = 0;
					break;
				}
			}
		}
	}
    else if(haveOne < K){ //已有的小于需要的就从前面补
    	int flag =  K - haveOne;
		while(flag--){
			for(int i = 0; i < Cnt; i++){
				if(maxNum[i] == 0){
					maxNum[i] = 1;
                    pos[haveOne++] = i;
					break;
				}
			}
		}
    }
	haveOne = K;
	/\*for(int i = 0; i < haveOne; i++){
 cout << pos[i];
 }\*/
	dfs(haveOne-1);
	cout << ans;
	return 0;
} 

试题 I: 翻转括号序列[25 分]

时间限制: 2.0s 内存限制: 512.0MB

【问题描述】

给定一个长度为 _n _的括号序列,要求支持两种操作:

  1. 将 [Li, Ri] 区间内(序列中的第 Li 个字符到第 Ri 个字符)的括号全部翻转(左括号变成右括号,右括号变成左括号)。
  2. 求出以 _Li _为左端点时,最长的合法括号序列对应的 _Ri _(即找出最大的

Ri 使 [Li, Ri] 是一个合法括号序列)。

【输入格式】

输入的第一行包含两个整数 n, m,分别表示括号序列长度和操作次数。第二行包含给定的括号序列,括号序列中只包含左括号和右括号。
接下来 m 行,每行描述一个操作。如果该行为 “1 Li Ri”,表示第一种操作,区间为 [Li, Ri] ;如果该行为 “2 Li” 表示第二种操作,左端点为 Li。
【输出格式】
对于每个第二种操作,输出一行,表示对应的 Ri。如果不存在这样的 Ri,请输出 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 _≤ 106, 1 ≤ _m _≤ 2 × 105。

【解题思路: 压缩并查集+提前标记 <=100%】

因为每次查都会浪费时间,而且用例很大,所以每次进行调转操作后,就记录每个点对应的匹配点在哪
此时又有一种情况()()() 此时的1 3 5都是以6截至,本来应该是12 34 56匹配,但题目是最远匹配处,所以要延申后面的匹配
所以看成正常匹配时fa[1] = 2,fa[2] = 0,fa[3] = 4,fa[4] = 6;
而当fa[1]匹配时查看匹配点2右边的点是否还继续匹配,
fa[1] + 1表示第一个匹配的括号的右边第一个位
fa[ fa[1] + 1 ]表示看下右边还有没有继续匹配的
匹配的话就继续延申下去,找到头,然后返回路上都
fa[1] == 2 fa[2+1]!=0 继续
fa[3] == 4 fa[4+1]!=0 继续
fa[5] == 6 fa[6] = 0 回退 return 6
fa[3] = 6
fa[1] = 6
置换就直接换就行

#include<bits/stdc++.h>
#include<iostream>
#define ll long long
using namespace std;

map<int ,int > mp; 
string str;
int num[1000005];
int n,m;

//初始化
void init(){
	mp.clear();
	stack<int> sta;
	for(int i = 1; i <=n ; i++){
		if(num[i] == 1){
			sta.push(i);
		}else if(num[i] == 0 && sta.size()){
			int index = sta.top();
			if(index != 0){
				sta.pop();
				mp[index] = i;
			}
		}else if(num[i] == 0 && !sta.size()){
			sta.push(0);
		}
	}
}
//改造的压缩并查集
int findFa(int a,int deep){
	if(mp[a] == 0 && deep == 0){
		return 0;
	}
	if(mp[a] == 0){
		return a - 1;
	}
	mp[a] =findFa(mp[a]+1,deep+1);
	return mp[a];
}
int main(){

	cin >> n >> m;
	cin >> str;
    //处理输入变成01串,方便倒置 !str[i]就行
	for(int i = 0;i < n; i++){
		if(str[i] == '('){
			num[i+1] = 1;
		}else{
			num[i+1] = 0;
		}
	}
	init();
	for(int i = 0; i < m; i++){
		int flag;
		cin >> flag;
        //倒置
		if(flag == 1){
			int a,b;
			cin >> a >> b;
			for(int j = a; j <= b; j++){
				num[j] = !num[j];
			}
			init();
		}else{
            //直接查询
			int a;
			cin >> a;
			cout << findFa(a,0) << endl;
		}
	}
	return 0;
} 

试题 J: 异或三角[25 分]

时间限制: 1.0s 内存限制: 256.0MB

【问题描述】

给定 T 个数 n1, n2, · · · , nT ,对每个 ni 请求出有多少组 a, b, c 满足:

  1. 1 ≤ a, b, c ≤ ni;

a ⊕ b ⊕ c = 0,其中 ⊕ 表示二进制按位异或;
长度为 a, b, c 的三条边能组成一个三角形。

【输入格式】

输入的第一行包含一个整数 T 。

接下来 T 行每行一个整数,分别表示 n1, n2, · · · , nT 。

【输出格式】

输出 T 行,每行包含一个整数,表示对应的答案。

【样例输入】

2

6

114514

【样例输出】

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

给定 T 个数 n1, n2, · · · , nT ,对每个 ni 请求出有多少组 a, b, c 满足:

  1. 1 ≤ a, b, c ≤ ni;

a ⊕ b ⊕ c = 0,其中 ⊕ 表示二进制按位异或;
长度为 a, b, c 的三条边能组成一个三角形。

【输入格式】

输入的第一行包含一个整数 T 。

接下来 T 行每行一个整数,分别表示 n1, n2, · · · , nT 。

【输出格式】

输出 T 行,每行包含一个整数,表示对应的答案。

【样例输入】

2

6

114514

【样例输出】

[外链图片转存中…(img-oJKEm0GL-1715519088249)]
[外链图片转存中…(img-rmAbkxXb-1715519088249)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 30
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值