2021年第十二届蓝桥杯A组国赛(C/C++)

  • Post author:
  • Post category:其他



A. 纯质数(5分)



答案:1903


在这里插入图片描述


思路:



欧拉筛。

由于20210605是一个八位数,即使是埃及筛的O(nlognlogn)也是会挺慢的,当然本题只需要求出结果即可;所以在不会欧拉筛的情况下可以利用埃及筛或O(n^2)的暴力判断素数。我使用的是

时间复杂度为O(n)的欧拉筛;然后枚举1~20210605之间每一个素数,判断其每一位是否是素数,若是,则结果加一。

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const int maxn=3e7+10;
const int N=20210605;
int prime[maxn],vis[maxn];
int ans=0,cnt=0;
bool check(int x){
	if(x==2||x==3||x==5||x==7) return true;
	return false;
}
void pre(){
	memset(vis,0,sizeof(vis));
	for(int i=2;i<=N;i++){
		if(!vis[i]){
			prime[++cnt]=i; vis[i]=1;
		}
		for(int j=1;j<=cnt&&i*prime[j]<=N;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0) break;
		}
	}
}
int main(){
	pre();
    for(int i=1;i<=cnt;i++){
    	int x=prime[i],flag=1;
    	while(x){
    		if(!check(x%10)) flag=0;
    		x/=10;
		}
		if(flag) ans++;
	}
	cout<<ans<<endl;
	return 0;
} 


B. 完全日期(5分)



答案:977


在这里插入图片描述


思路:



简单模拟题。


首先8位数的各位数之和最大为72,即99999999;

我们首先将1~72的平方标记为1;然后枚举2001年1月1日到2021年12月31日的每一天,判断该数字组合的各位数字之和是否是完全平方数;若是,则结果加一;注意判断闰年,且闰年的2月份有29天;

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
const int maxn=1e4+10;
const int N=20210605;
int vis[maxn],yue[20]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int ans=0;
bool isrun(int year){
	if((year%400==0)||(year%4==0&&year%100!=0)) return true;
	return false;
}
int f(int x){
	int res=0;
	while(x){
		res+=x%10;
		x/=10;
	}
	return res;
}
bool check(int a,int b,int c){
	int res=0;
	res=f(a)+f(b)+f(c);
	if(vis[res]) return true;
	return false;
}
int main(){
	memset(vis,0,sizeof(vis));
    for(int i=1;i<=72;i++) vis[i*i]=1;
    for(int i=2001;i<=2021;i++){
    	for(int j=1;j<=12;j++){
    		if(isrun(i)) yue[2]=29;
    		else yue[2]=28;
    		for(int k=1;k<=yue[j];k++){
    			if(check(i,j,k)) ans++;
			}
		}
	}
	cout<<ans<<endl;
	return 0;
} 


C. 最小权值(10分)



答案:2653631372


在这里插入图片描述


思路:



线性dp。


由于每个节点的权值只和它的左右子树权值和左右子树结点个数有关;

我们用dp[i]表示有i个结点的二叉树的最小权值

;所以

状态转移方程便是


dp[i]=min{1+2* dp[j]+3* dp[i-j-1]+(j* j *(i-j-1))}(j<=i-1)

;(j为左子树的结点个数,i-j-1为右子树的结点个数)。

#include<bits/stdc++.h>
using namespace std;
#define INF 1e18
typedef long long LL;
const int maxn=1e4+10;
const int N=2021;
LL dp[N];
int main(){
	for(int i=1;i<=N;i++) dp[i]=INF; 
	dp[1]=1;
	for(int i=1;i<=N;i++){
		for(int j=0;j<=i-1;j++)
		dp[i]=min(dp[i],1+2*dp[j]+3*dp[i-j-1]+j*j*(i-j-1));
	}
	cout<<dp[N]<<endl;
	return 0;
} 


D. 覆盖(10分)



答案:


在这里插入图片描述


思路:


额不会。



E.123(15分)

试题 E: 123

时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分

【问题描述】

小蓝发现了一个有趣的数列,这个数列的前几项如下:

1, 1, 2, 1, 2, 3, 1, 2, 3, 4, …

小蓝发现,这个数列前 1 项是整数 1,接下来 2 项是整数 1 至 2,接下来

3 项是整数 1 至 3,接下来 4 项是整数 1 至 4,依次类推。

小蓝想知道,这个数列中,连续一段的和是多少。

【输入格式】

输入的第一行包含一个整数 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 ≤ 10^6。

对于 70% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ li ≤ ri ≤ 10^9。

对于 80% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ li ≤ ri ≤ 10^12。

对于 90% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ li ≤ ri ≤ 10^12。

对于所有评测用例,1 ≤ T ≤ 100000, 1 ≤ li ≤ ri ≤ 10^12。


思路:



数学规律+思维。


我们知道,1+2+3+…+n=n*(n+1)/2。

所以可以用数组a[maxn]记录1~i个数相加的值,再用数组sum[maxn]记录数组a的前缀和

;要求区间[l,r]之间每个数的和,我们可以

用前缀和求出,即结果为get_res( r)- get_res(l-1)

;该函数get_res (x)用来求数列1 ~ x个数的和。我们知道,对于数列中第c个数,它会

满足等式 c== n*(n+1)/2+b; (n代表的是数列中第c个数在包含前n项,b为第n+1项中的第b个数);所以对于函数get_res( x)的结果便是sun[n]+b*(b+1)/2。

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
typedef long long LL;
const int maxn=1e7+100;
LL a[maxn],sum[maxn];
LL get_res(LL x){
	LL n=upper_bound(a+1,a+maxn-99,x)-a-1,temp=x-n*(n+1)/2,res=0;
	res=temp*(temp+1)/2+sum[n];
	if(x==0) return 0;
	return res;
}
void pre(){
	for(LL i=1;i<=maxn-100;i++){
		a[i]=i*(i+1)/2; 
		sum[i]=sum[i-1]+a[i];
	}
}
int main(){
	int t; cin>>t;
	pre();
	while(t--){
		LL l,r,ans=0;
		cin>>l>>r;
        ans=get_res(r)-get_res(l-1);
        cout<<ans<<endl;
	}
	return 0;
} 


F.异或变换(15分)


试题 F: 异或变换

时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分

【问题描述】


小蓝有一个 01 串 s = s1 s2 s3 · · · sn。

以后每个时刻,小蓝要对这个 01 串进行一次变换。每次变换的规则相同。

对于 01 串 s = s1 s2 s3 · · · sn,变换后的 01 串 s′ = s1′s2′s3′· · · sn′为:

s1′ = s1;

si′ = 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 ≤ 10^9。

对于所有评测用例,1 ≤ n ≤ 10000, 1 ≤ t ≤ 10^18。


思路:



第一种思路:暴力骗分。


由于40%的样例满足n<=100,t<=1000;

直接双重循环遍历便可骗到至少40%的样例分。



第二种思路:找规律。


或许会出现循环节,但是弱弱不会。。。。。弱弱看完大佬的思路,虽然不知道为啥

循环节大小是>=n的二次幂整数

,但是按照大佬的思路还是很好写出代码的。


暴力骗分:至少40分

#include<bits/stdc++.h>
using namespace std;
#define INF 1e18
typedef long long LL;
const int maxn=1e4+10;
char s[maxn],str[maxn];
int main(){
    iostream::sync_with_stdio(false);
    int n,t; cin>>n>>t;
    s[0]='0';
    for(int i=1;i<=n;i++) cin>>s[i];
    for(int i=1;i<=t;i++){
    	str[0]='0';
    	for(int j=1;j<=n;j++) str[j]=(s[j]-'0')^(s[j-1]-'0')+'0';
		str[n+1]='\0';
		memcpy(s+1,str+1,sizeof(str));
	}
	for(int i=1;i<=n;i++) cout<<s[i];
	return 0;
} 


AC代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 1e18
typedef long long LL;
const int maxn=1e4+10;
char s[maxn];
LL n,t,x=0;
void get_res(int temp){
	while(temp/2){
		x++;
		temp>>=1;
	}
	if(n%(1<<x)!=0) x++;
}
int main(){
    iostream::sync_with_stdio(false);
    cin>>n>>t;
    for(int i=1;i<=n;i++) cin>>s[i];
    get_res(n);
    t=t%(1<<x);
    for(LL i=1;i<=t;i++){
    	for(LL j=n;j>=2;j--) s[j]=(s[j]-'0')^(s[j-1]-'0')+'0';
	}
	for(LL i=1;i<=n;i++) cout<<s[i];
	return 0;
} 



G. 冰山(20分)


试题 G: 冰山

时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分

【问题描述】


一片海域上有一些冰山,第 i 座冰山的体积为 Vi。

随着气温的变化,冰山的体积可能增大或缩小。第 i 天,每座冰山的变化量都是 Xi。当 Xi > 0 时,所有冰山体积增加 Xi;当 Xi < 0 时,所有冰山体积减少 −Xi;当 Xi = 0 时,所有冰山体积不变。

如果第 i 天某座冰山的体积变化后小于等于 0,则冰山会永远消失。

冰山有大小限制 k。如果第 i 天某座冰山 j 的体积变化后 Vj 大于 k,则它会分裂成一个体积为 k 的冰山和 Vj − k 座体积为 1 的冰山。

第 i 天结束前(冰山增大、缩小、消失、分裂完成后),会漂来一座体积为Yi 的冰山(Yi = 0 表示没有冰山漂来)。

小蓝在连续的 m 天对这片海域进行了观察,并准确记录了冰山的变化。小蓝想知道,每天结束时所有冰山的体积之和(包括新漂来的)是多少。


由于答案可能很大,请输出答案除以 998244353 的余数。



【输入格式】


输入的第一行包含三个整数 n, m, k,分别表示初始时冰山的数量、观察的天数以及冰山的大小限制。

第二行包含 n 个整数 V1, V2, · · · , Vn,表示初始时每座冰山的体积。

接下来 m 行描述观察的 m 天的冰山变化。其中第 i 行包含两个整数 Xi, Yi,意义如前所述。


【输出格式】


输出 m 行,每行包含一个整数,分别对应每天结束时所有冰山的体积之和除以 998244353 的余数。


【样例输入】


1 3 6

1

6 1

2 2

-1 1


【样例输出】


8

16

11


【样例说明】


在本样例说明中,用 [a1, a2, · · · , an] 来表示每座冰山的体积。

初始时的冰山为 [1]。

第 1 天结束时,有 3 座冰山:[1, 1, 6]。

第 2 天结束时,有 6 座冰山:[1, 1, 2, 3, 3, 6]。

第 3 天结束时,有 5 座冰山:[1, 1, 2, 2, 5]。


【评测用例规模与约定】


对于 40% 的评测用例,n, m, k ≤ 2000;

对于 60% 的评测用例,n, m, k ≤ 20000;

对于所有评测用例,1 ≤ n, m ≤ 100000, 1 ≤ k ≤ 10^9, 1 ≤ Vi ≤ k, 0 ≤ Yi ≤ k, −k ≤ Xi ≤ k。


思路:


弱弱比较菜,

只会暴力骗分

,由于没有提交的oj,弱弱也不知道能骗多少分,估计是40%左右吧。

暴力骗分:

#include<bits/stdc++.h>
using namespace std;
#define INF 1e18
typedef long long LL;
const int maxn=1e5+10;
LL ans=0,mod=998244353;
vector<LL>g;
int main(){
    iostream::sync_with_stdio(false);
    int n,m,k; cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
    	LL v; cin>>v; 
		g.push_back(v); ans+=v;
	}
	for(int i=1;i<=m;i++){
		LL x,y,len=g.size(); cin>>x>>y;
	    for(int j=0;j<len;j++){
	    	g[j]+=x;
	    	if(g[j]<=0) g.erase(j+g.begin());
	    	else if(g[j]>k){
	    		for(int cnt=1;cnt<=g[j]-k;cnt++) g.push_back(1);
	    		g[j]=k;
			}
		}
		if(y>0) g.push_back(y);
		ans+=(x*len+y)%mod;
		cout<<ans<<endl;
	}
	return 0;
} 


H.翻转括号序列(20分)


试题 H: 翻转括号序列

时间限制: 2.0s 内存限制: 512.0MB 本题总分:20 分

【问题描述】


给定一个长度为 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 ≤ 10^6, 1 ≤ m ≤ 2 × 10^5。


思路:


弱弱又是选择了

暴力骗分

,蓝桥杯不愧是暴力杯,太适合弱弱骗分了。。。(当然是弱弱太菜了)。估计通过的样例在40%左右。


时间复杂度为O(n*m)

暴力骗部分分:

#include<bits/stdc++.h>
using namespace std;
#define INF 1e18
typedef long long LL;
const int maxn=1e6+10;
char s[maxn];
int main(){
    iostream::sync_with_stdio(false);
    int n,m; cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>s[i];
    for(int i=1;i<=m;i++){
    	int opt,l,r; cin>>opt;
    	if(opt==1){
    		cin>>l>>r;
    		for(int j=l;j<=r;j++) s[j]=(s[j]=='('?')':'(');
		}
		else{
			cin>>l;
			int pos=0,LL=0,RR=0;
			for(int j=l;j<=n;j++){
				if(s[j]=='(') LL++;
				else RR++;
				if(LL==RR) pos=j;
				else if(LL<RR) break;
			}
			cout<<pos<<endl;
		}
	}
	return 0;
} 


I. 异或三角(25分)


试题 I: 异或三角

时间限制: 1.0s 内存限制: 256.0MB 本题总分:25 分

【问题描述】


给定 T 个数 n1, n2, · · · , nT,对每个 ni 请求出有多少组 a, b, c 满足:1. 1 ≤ a, b, c ≤ ni;2. a ⊕ b ⊕ c = 0,其中 ⊕ 表示二进制按位异或;3. 长度为 a, b, c 的三条边能组成一个三角形。


【输入格式】


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

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


【输出格式】


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


【样例输入】


2

6

114514


【样例输出】


6

11223848130


【评测用例规模与约定】


对于 10% 的评测用例,T = 1, 1 ≤ ni ≤ 200;

对于 20% 的评测用例,T = 1, 1 ≤ ni ≤ 2000;

对于 50% 的评测用例,T = 1, 1 ≤ ni ≤ 2^20;

对于 60% 的评测用例,1 ≤ T ≤ 100000, 1 ≤ ni ≤ 2^20;

对于所有评测用例,1 ≤ T ≤ 100000, 1 ≤ ni ≤ 2^30。


思路:


当然是

暴力骗分

了。 由于a^ b ^c== 0可知,

a ^b==c;因此暴力的时候可以将时间复杂度从O(n ^3)降到O(n ^2)

。至少通过20%的样例吧。

暴力骗分:

#include<bits/stdc++.h>
using namespace std;
#define INF 1e18
typedef long long LL;
const int maxn=1e6+10;
char s[maxn];
bool check(LL a,LL b,LL c){
	if(a+b<=c||a+c<=b||b+c<=a) return true;
	if(abs(a-b)>=c||abs(a-c)>=b||abs(b-c)>=a) return true;
	return false;
}
int main(){
    iostream::sync_with_stdio(false);
    int t; cin>>t;
    while(t--){
    	LL n,ans=0; cin>>n;
    	for(LL a=1;a<=n;a++){
    		for(LL b=1;b<=n;b++){
    			LL c=a^b;
    			if(c<=0||check(a,b,c)) continue;
    			ans++;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
} 



J. 积木(25分)


试题 J: 积木

时间限制: 2.0s 内存限制: 256.0MB 本题总分:25 分

【问题描述】


小蓝有大量正方体的积木(所有积木完全相同),他准备用积木搭一个巨大的图形。

小蓝将积木全部平铺在地面上,而不垒起来,以便更稳定。他将积木摆成一行一行的,每行的左边对齐,形成最终的图形。

第一行小蓝摆了 H1 = w 块积木。从第二行开始,第 i 行的积木数量 Hi 都至少比上一行多 L,至多比上一行多 R(当 L = 0 时表示可以和上一行的积木数量相同),即 Hi−1 + L ≤ Hi ≤ Hi−1 + R。

给定 x, y 和 z,请问满足以上条件的方案中,有多少种方案满足第 y 行的积木数量恰好为第 x 行的积木数量的 z 倍。


【输入格式】


输入一行包含 7 个整数 n, w, L, R, x, y, z,意义如上所述。


【输出格式】


输出一个整数, 表示满足条件的方案数,答案可能很大,请输出答案除以

998244353 的余数。


【样例输入 1】


5 1 1 2 2 5 3


【样例输出 1】


4


【样例输入 2】


233 5 1 8 100 215 3


【样例输出 2】


308810105


【评测用例规模与约定】


对于 10% 的评测用例,1 ≤ n ≤ 10, 1 ≤ w ≤ 10, 0 ≤ L ≤ R ≤ 3;

对于 20% 的评测用例,1 ≤ n ≤ 20, 1 ≤ w ≤ 10, 0 ≤ L ≤ R ≤ 4;

对于 35% 的评测用例,1 ≤ n ≤ 500, 0 ≤ L ≤ R ≤ 10;

对于 50% 的评测用例,1 ≤ n ≤ 5000, 0 ≤ L ≤ R ≤ 10;

对于 60% 的评测用例,1 ≤ n ≤ 20000, 0 ≤ L ≤ R ≤ 10;

对于 70% 的评测用例,1 ≤ n ≤ 50000, 0 ≤ L ≤ R ≤ 10;

对于 85% 的评测用例,1 ≤ n ≤ 300000, 0 ≤ L ≤ R ≤ 10;

对于所有评测用例,1 ≤ n ≤ 500000, 1 ≤ w ≤ 10^9,

0 ≤ L ≤ R ≤ 40, 1 ≤ x < y ≤ n, 0 ≤ z ≤ 10^9。



版权声明:本文为qq_51467306原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。