第十二屆藍橋杯大賽軟件賽決賽題解


題目下載鏈接
注意,以下答案均為作者本人的答案,不是官方答案!!!(也就是說,可能(多半)是錯的)

填空預覽

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\n",i);
	}
	printf("%lld\n",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\n",loc_year,loc_mon,loc_day);
				}
			}
		}
	}
	printf("op == %lld, ans = %lld\n",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\n",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\n",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\n",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\n",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\n",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\n");
			continue;
		}
		if(n > 1000) {
			printf("%d\n",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\n",i,j,k);
				}
			}
		}
		printf("%lld\n",ans);
		
	}
	
	return 0;
}
/*

*/

總結

此次藍橋杯國賽簽到題還是比較友好,難度適中?,有難題也有簡單題,還有就是每次遇到日期都有一種做不對的感覺,今天仔仔細細檢查了好幾次,不知道錯沒有,接着就是填空最后一個emmm,怎么說呢,蒙了個完全二叉樹
E題就是語法題,F題之前做過類似的題目,記得是直接推了一個公式來着,這次沒推出來,直接寫的預處理了,G題第一眼給我的感覺就是肯定有周期,果不其然,發現規律后還是不難,后面的H,IJ三題,感覺都是有一定難度,I題感覺在哪見過,做法應該是線段樹維護區間
但是怕線段樹寫錯了,就寫了樹狀數組暴力騙分了=_=,J一點想法也沒有,就暴力了一下,總體而言我感覺我在數據結構和數論方面掌握的還是不夠,要加強線段樹,樹結構,組合數方面的練習了,感覺這次國賽沒了T^T。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM