前言
蓝桥杯赛制介绍
蓝桥杯采用 IO赛制,比赛开始后即可查看题目。比赛通常包含 一至两道填空题 和若干 编程大题。在答题过程中,选手可以 多次提交答案,但最终成绩以 最后一次提交为准。提交后,系统仅显示“提交成功”,不会提示是否通过或得分情况。
-
填空题:只需输出答案,评分只有 0分或满分 两种情况。
-
编程大题:根据最后一次提交的代码通过的测试用例数量来判分。
便捷小技巧
在比赛时,机房电脑的考试系统通常会提供 C++使用文档,选手可以查阅一些函数的使用方法。此外,电脑自带的 计算器、Excel表格和日历 等工具也可以使用。其中,计算器的 程序员模式 对进制转换可能有所帮助;Excel和日历则可以在填空题难以解答时用于人工模拟,例如处理一些复杂的日期问题。
打表操作(“骗分”技巧)
由于编程大题的得分依据是通过的测试用例数量,因此在遇到不会做的题目时,可以尝试以下方法:
-
暴力解法:尽可能通过暴力方法多通过一些测试用例。
-
打表输出样例:如果暴力方法也难以实现,可以手动模拟一些输入,手算出结果并输出。
例如,如果题目输入较为简单(如只有一个变量 n
),可以采用如下打表方式:
cin >> n; if (n == 0) cout << "手算的结果"; else if (n == 1) cout << "手算的结果"; else if (n == 2) cout << "手算的结果"; else if (n == 3) cout << "手算的结果"; // 根据需要继续补充更多情况
虽然这种方法看起来有些“投机取巧”,但在比赛这种紧张的环境下,为了争取更多分数,这种策略也是可以理解的。毕竟,有时候省一等奖可能就差这么几分呢!:D
基础常识
1. 素数(质数)
-
定义:素数(或质数)是指在大于1的自然数中,除了1和它本身以外不再有其他因数的数。
-
特点:
-
最小的素数是2,且2是唯一的偶数素数。
-
6k ± 1 规律:除了2和3之外的所有素数都可以表示为 6k±1 的形式。
-
所有其他素数都是奇数。
-
1不是素数,也不是合数。
-
-
判断方法:
-
试除法:判断一个数 n 是否为素数,可以尝试从2到 n 的所有整数是否能整除 n。如果都不能整除,则 n 是素数。
-
欧拉筛:后文介绍。
-
2. 闰年
-
定义:闰年是为了弥补因人为历法规定造成的年度天数与地球实际公转周期的时间差而设立的。
-
特征:
-
普通闰年:公历年份是4的倍数,且不是100的倍数。
-
世纪闰年:公历年份是400的倍数。
-
-
判断方法:
-
如果年份能被4整除但不能被100整除,或者能被400整除,则是闰年。
-
完全平方数
-
定义:一个数可以表示为某个整数的平方。
-
判断方法:检查一个数的平方根是否为整数。
-
应用:在涉及平方数的题目中,可以直接判断一个数是否为完全平方数。
常用函数
万能头文件
#include <bits/stdc++.h> // 包含大部分STL和C标准库 using namespace std; // 方便使用标准库
初始化函数 memset
int arr[10]; memset(arr, 0, sizeof(arr)); // 将整个数组初始化为0 memset(arr, 0xFF, sizeof(arr)); // 将整个数组的每个字节设置为0xFF(注意:每个字节为-1)
十进制转二进制便捷函数(bitset
)
int num = 123; // 十进制数 bitset<32> binary(num); // 转换为二进制,32表示位数 cout << "二进制表示为:" << binary << endl;
二进制转十进制便捷函数(bitset
)
string binaryStr = "0011"; int s = bitset<32>(binaryStr).to_ulong(); cout << s << endl; // 输出转换后的十进制数
快速求一个数的位数
int n = 1000; int x = log10(n) + 1; // 计算位数 cout << x << endl; // 输出4
最小公倍数
// 最小公倍数函数实现 int lcm(int a, int b) { return a / gcd(a, b) * b; }
最大公约数
// 最大公约数函数实现 int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
连续读数(不知道输入个数的情况)
int a[1000]; // 假设数组大小足够 int cnt = 0; while (scanf("%d", &a[cnt]) != EOF) { cnt++; }
STL常用数据类型
vector
动态数组
vector<int> vec;//也可以和普通数组使用的方法一样 vec.push_back(10); // 添加元素到末尾 vec.insert(vec.begin(), 5); // 在开头插入元素 int first = vec[0]; // 通过下标访问 vec.pop_back(); // 删除最后一个元素 vec.erase(vec.begin() + 1); // 删除下标为1的元素
pair
(对组)
pair<int, string> myPair(1, "one"); int key = myPair.first; string value = myPair.second;
优先队列(大根堆)
priority_queue<int> pq; // 默认最大堆 pq.push(10); pq.push(5); pq.push(20); cout << "队列顶部元素(最大值): " << pq.top() << endl; // 访问队列顶部元素 pq.pop(); // 删除队列顶部元素 cout << "删除顶部元素后,新的顶部元素: " << pq.top() << endl;
优先队列(小根堆)
priority_queue<int, vector<int>, greater<int>> minPq; // 小根堆 minPq.push(10); minPq.push(5); minPq.push(20); cout << "队列顶部元素(最小值): " << minPq.top() << endl; // 访问队列顶部元素 minPq.pop(); // 删除队列顶部元素 cout << "删除顶部元素后,新的顶部元素: " << minPq.top() << endl;
map
(有序字典)O(logn)
map<int, string> myMap; myMap[1] = "one"; // 插入键值对 myMap.insert(make_pair(2, "two")); // 插入键值对 string value = myMap[1]; // 通过键访问 for (auto pair : myMap) { cout << pair.first << ": " << pair.second << endl; }
unordered_map
(无序字典)O(1)∼O(n)
unordered_map<int, string> umap; umap[1] = "one"; umap.insert(make_pair(2, "two")); string value = umap[1]; for (auto pair : umap) { cout << pair.first << ": " << pair.second << endl; }
set
(集合)
-
特点:元素唯一,可以用来去重;默认升序排列。
set<int> mySet; // 定义一个存储整数的集合 mySet.insert(10); mySet.insert(5); mySet.insert(20); mySet.insert(10); // 重复插入不会成功 if (mySet.find(10) != mySet.end()) { cout << "10 在集合中" << endl; } else { cout << "10 不在集合中" << endl; } mySet.erase(5); // 删除元素5 for (int value : mySet) { cout << value << " "; } if (mySet.empty()) { cout << "集合为空" << endl; } else { cout << "集合不为空" << endl; }
STL排序函数sort
vector<int> a(n); sort(a.begin(),a.end());//从小到大排序 sort(a.begin(),a.end(),greater<int>());//从大到下排序
查找子串find()
返回子串在主串中第一次出现的位置,没找到返回-1
string str = "Hello,wello!"; string substr="ello"; int index = str.find(substr); if(index!=-1) cout<<index; else cout<<"NO found";
分割子串substr
string str = "Hello, world!"; string s= str.substr(3,2);//从下标3开始,提取两个字母 cout<<s;//lo
string str = "Hello, world!"; string s= str.substr(3);//从下标3开始的子串 cout<<s;//lo, world!
实用算法模板
欧拉筛(线性筛)
int primes[N], cnt; bool st[N]; void init(int n) { for (int i = 2; i <= n; i++) { if (!st[i]) primes[cnt++] = i; for (int j = 0; primes[j] <= n / i; j++) { st[i * primes[j]] = true; // 用质因子筛 if (i % primes[j] == 0) break; // 当走到这里时,这个质因子是i能整除的最小的了,我们不用判断后面的质因子了 // 如果不加优化,那么这个数就会被重复筛 } } }
单源最短路径:堆优化版Dijkstra:
#include <bits/stdc++.h> using namespace std; typedef pair<int, int> PII; const int N = 100010, M = 200010; int h[N], e[M], ne[M], w[M], idx; int dist[N]; bool st[N]; int n, m, s; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } void dijkstra(int s) { memset(dist, 0x3f, sizeof dist); dist[s] = 0; priority_queue<PII, vector<PII>, greater<PII>> heap; heap.push({0, s}); while (heap.size()) { PII t = heap.top(); heap.pop(); int v = t.second; if (st[v]) continue; st[v] = true; for (int i = h[v]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j] > dist[v] + w[i]) { dist[j] = dist[v] + w[i]; heap.push({dist[j], j}); } } } for (int i = 1; i <= n; i++) cout << dist[i] << ' '; } int main() { cin >> n >> m >> s; memset(h, -1, sizeof h); for (int i = 1; i <= m; i++) { int a, b, c; cin >> a >> b >> c; add(a, b, c); } dijkstra(s); return 0; }
最小生成树
#include <bits/stdc++.h> using namespace std; const int N = 5010, M = 200010; struct Edge { int a, b, c; bool operator< (const Edge& E) const { return c < E.c; } } edges[M]; int p[N]; int n, m; int ans; int find(int x) { return p[x] = x == p[x] ? p[x] : find(p[x]); } bool kruskal() { for (int i = 1; i <= n; i++) p[i] = i; int cnt = 0; for (int i = 1; i <= m; i++) { int a = edges[i].a, b = edges[i].b, w = edges[i].c; if (find(a) == find(b)) continue; p[find(a)] = find(b); ans += w; cnt++; } if (cnt < n - 1) return true; return false; } int main() { cin >> n >> m; for (int i = 1; i <= m; i++) { int a, b, c; cin >> a >> b >> c; edges[i] = {a, b, c}; } sort(edges + 1, edges + 1 + m); if (kruskal()) puts("orz"); else cout << ans << endl; return 0; }
二维前缀和
#include <bits/stdc++.h> using namespace std; const int N=1010; int n,m,q; int a[N][N],s[N][N]; int main() { cin >> n >> m >> q; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { cin >> a[i][j]; s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]; // 二维 前缀和的初始化 } while (q--) { int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl; } return 0; }
二维差分
#include <bits/stdc++.h> using namespace std; const int N=1010; int n,m,q; int a[N][N],b[N][N]; void modify(int x1,int y1,int x2,int y2,int c){ b[x1][y1]+=c; b[x2+1][y1]-=c; b[x1][y2+1]-=c; b[x2+1][y2+1]+=c; } int main(){ cin>>n>>m>>q; for(int i=0;i<=n;i++) for (int j = 1; j <= m; j++) { cin >> a[i][j]; //b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1]; // 初 modify(i, j, i, j, a[i][j]); // 初始化差分数组 } while (q--) { int x1, y1, x2, y2, c; cin >> x1 >> y1 >> x2 >> y2 >> c; modify(x1, y1, x2, y2, c); } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1]; // 求前缀和 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) cout << b[i][j] << ' '; cout << endl; } return 0; }