蓝桥杯2023年第十四届省赛真题题解
日期统计
纯暴力 + 剪枝(注意日期年月日的一些特性)
#include <iostream>
#include <unordered_set>
using namespace std;
const int nums[] = {0, 5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7, 5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9, 2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6, 8, 6, 3, 3, 8, 5, 1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6, 1, 4, 0, 1,
0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3};
const int n = 100;
const int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
unordered_set<string> st;
int main(int argc, const char * argv[]) {
int sum = 0;
for (int a = 1; a <= n; ++a) {//year
if (nums[a] == 2) {
for (int b = a + 1; b <= n; ++b) //year
if (nums[b] == 0) {
for (int c = b + 1; c <= n; ++c) {//year
if (nums[c] == 2) {
for (int d = c + 1; d <= n; ++d) {//year
if (nums[d] == 3) {
for (int e = d + 1; e <= n; ++e) {//month
if (nums[e] == 0 || nums[e] == 1) {
for (int f = e + 1; f <= n; ++f) {//month
if ((nums[e] == 0 && nums[f] != 0) || (nums[e] == 1 && nums[f] <= 2)) {
for (int g = f + 1; g <= n; ++g) {//day
if (nums[g] <= 3) {
for (int h = g + 1; h <= n; ++h) {//day
if (nums[g] == 0 && nums[h] == 0) {
continue;
}
string str = "2023";
int month = nums[e] * 10 + nums[f];
int day = nums[g] * 10 + nums[h];
str += to_string(nums[e]) + to_string(nums[f]);
str += to_string(nums[g]) + to_string(nums[h]);
if (day <= days[month]) {
if (st.count(str)) {
continue;
}
st.insert(str);
puts(str.c_str());
++sum;
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
printf("%d\n",sum);
return 0;
}
01串的熵
模拟题
#include <iostream>
#include <cmath>
using namespace std;
const int n = 23333333;
const double eps = 1e-4;
const double ans = 11625907.5798;
double f(int sum1, int sum0){
double res = 0;
double p1 = (double)sum1 / (double)n;
double p0 = (double)sum0 / (double)n;
res += p1 * sum1 * log2(p1);
res += p0 * sum0 * log2(p0);
return res;
}
int main()
{
for (int i = 0; i <= n >> 1; ++ i)
{
if (fabs(-f(n - i, i) - ans) <= eps)
{
cout << i;
break;
}
}
return 0;
}
冶炼金属
解法一:思维
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e4 + 10;
int n;
int maxx = 0, minn = 1e9;
int main()
{
cin >> n;
for (int i = 0; i < n; ++ i)
{
int a, b;
cin >> a >> b; // a / v = b; a = b * v;
maxx=max(maxx,a/(b+1) + 1) , minn=min(minn,a/b);
}
cout << maxx << " " << minn;
return 0;
}
解法二:二分
飞机降落
dfs暴力
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 11;
int t[N], d[N], l[N];
int st[N];
int n;
int ans;
bool dfs(int cnt, int time)
{
if (cnt == n)
{
return true;
}
for (int i = 0; i < n; ++ i)
{
if (st[i] == 0 && time <= t[i] + d[i])
{
st[i] = 1;
cout << dfs(cnt + 1, max(time, t[i]) + l[i]);
st[i] = 0;
}
}
return false;
}
void solution()
{
ans = 0;
memset(st, 0, sizeof st);
memset(t, 0, sizeof t);
memset(l, 0, sizeof l);
memset(d, 0, sizeof d);
cin >> n;
for (int i = 0; i < n; ++ i)
{
cin >> t[i] >> d[i] >> l[i];
}
if (dfs(0, 0)) cout << "YES" << endl;
else cout << "NO" << endl;
return;
}
int main()
{
int T;
cin >> T;
while (T --) solution();
return 0;
}
接龙数列
dp
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int dp[N];
int main()
{
int n;
cin >> n;
int ans = 0;
for (int i = 0; i < n; ++ i)
{
string s;
cin >>s;
int a = s[0] - '0', b = s.back() - '0';
dp[b] = max(dp[a] + 1, dp[b]);
ans = max(ans, dp[b]);
}
cout <<n - ans;
return 0;
}
岛屿个数
解法一:染色 + 合并 + 统计
和常规的染色题目相比, 本题多了一个合并的步骤, 染色的时候通过八联通将各个岛屿分开, 并且将海水标记为2, 如果后续遍历地图的时候遇到了0, 说明这里是海水八联通进不来的地方, 直接用岛屿的标志1去合并这一整个镂空的岛屿 , 类似这样把这个岛里面铺满;
如果像下面这样, 外面的岛没有把里面的岛完全包围的情况, 那么也符合我们的预期, 他们是两个岛屿, 在dfs4(四联通)的时候会统计成两个岛屿
#include<iostream>
#include<cstring>
using namespace std;
const int N = 52;
char g[N][N];
int T, n, m;
int ans;
int dx8[] = {1, 0, -1, 0, 1, 1, -1, -1}, dy8[] = {0, 1, 0, -1, 1, -1, 1, -1};
int dx4[] = {0, 1, 0, -1}, dy4[] = {1, 0, -1, 0};
void dfs4(int x, int y)
{
g[x][y] = '2';
int xx, yy;
for (int i = 0; i <4; ++ i)
{
xx = x + dx4[i], yy = y + dy4[i];
if (g[xx][yy] == '1' && xx >= 0 && xx <= n && yy >= 0 && yy <= m)
{
dfs4(xx, yy);
}
}
}
void dfs8(int x, int y)
{
g[x][y] = '2';
for (int i = 0; i < 8; ++ i)
{
int xx = x + dx8[i], yy = y + dy8[i];
if (g[xx][yy] == '0' && xx >= 0 && xx <= n + 1 && yy >= 0 && yy <= m + 1 )
{
dfs8(xx, yy);
}
}
}
int main()
{
cin >> T;
while (T --)
{
ans = 0;
cin >> n >> m;
//初始化
for (int i = 0; i < n + 2; ++ i)
for (int j = 0; j < m + 2; ++ j)
g[i][j] = '0';
// io
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++ j)
cin >> g[i][j];
//染海水
dfs8(0, 0);
//合并, 海水进不来的地方说明被岛屿全包围了, 那么直接变成1合并成一个岛屿
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= m; ++ j)
if (g[i][j] == '0') g[i][j] = '1';
}
//统计岛屿数量
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= m; ++ j)
if (g[i][j] == '1')
{
ans ++;
dfs4(i, j);
}
}
cout << ans << endl;
}
return 0;
}
解法二: 给岛屿染色, 统计有多少种不同的颜色
学习这个博主的
坑: 多组测试数据记得memset g 和st, 我就是没有memset st 一直wa;
思路:
- 给每个岛屿染上不同的颜色(用四联通 dfs4)
- 通过海水进行八连通dfs8遍历岛屿数目, 记得dfs8中将遍历过的海水从0 变为 -1 , 否则dfs会爆内存
#include<iostream>
#include<cstring>
using namespace std;
const int N = 52;
int g[N][N];
int T, n, m;
int ans, k = 2;
int dx8[] = {1, 0, -1, 0, 1, 1, -1, -1}, dy8[] = {0, 1, 0, -1, 1, -1, 1, -1};
int dx4[] = {0, 1, 0, -1}, dy4[] = {1, 0, -1, 0};
int st[N];
void dfs4(int x, int y)
{
g[x][y] = k;
int xx, yy;
for (int i = 0; i < 4; ++ i)
{
xx = x + dx4[i], yy = y + dy4[i];
if (g[xx][yy] == 1 && xx >= 1 && xx <= n && yy >= 1 && yy <= m)
{
dfs4(xx, yy);
}
}
}
void dfs8(int x, int y)
{
g[x][y] = -1;
for (int i = 0; i < 8; ++ i)
{
int xx = x + dx8[i], yy = y + dy8[i];
if (xx >= 0 && xx <= n + 1 && yy >= 0 && yy <= m + 1)
{
if (g[xx][yy] >= 2 && !st[g[xx][yy]])
{
st[g[xx][yy]] = 1;
ans ++;
}
if (g[xx][yy] == 0) dfs8(xx, yy);
}
}
}
int main()
{
cin >> T;
while (T --)
{
ans = 0;
k = 2;
cin >> n >> m;
//初始化
memset(g, 0, sizeof g);
memset(st, 0, sizeof st);
// io
for (int i = 1; i <= n; ++i)
{
string x;
cin >> x;
for (int j = 1; j <= m; ++ j)
{
g[i][j] = x[j - 1] - '0';
}
}
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= m; ++ j)
{
if (g[i][j] == 1)
{
dfs4(i, j);
k ++;
}
}
}
dfs8(0, 0);
cout << ans << endl;
}
return 0;
}
子串简写
后缀和
记得ans开long long
#include<iostream>
#include<string>
#include <queue>
using namespace std;
const int N = 5e5 + 10;
int k;
string s;
char c1, c2;
int cnt[N];
int main()
{
long long ans = 0;
cin >> k >> s >> c1 >> c2;
for (int i = s.size() - 1; i >= 0; --i)
{
cnt[i] = cnt[i + 1];
if (s[i] == c2) cnt[i] ++;
}
for (int i = 0; i + k - 1 < s.size(); ++ i)
{
if (s[i] == c1) ans += cnt[i + k - 1];
}
cout << ans;
return 0;
}
整数删除
数组模拟双向链表, 优先队列; 这题考的数据结构
注意模拟双向链表的时候 r[0] = 1; l[n + 1] = n;
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
typedef long long LL;
typedef pair<LL, LL> PLL;
int n, k;
LL a[N], l[N], r[N];
void del(LL i)
{
//把i的值加到i的左边和右边
a[l[i]] += a[i], a[r[i]] += a[i];
//i的左边指向i的右边, i的右边指向i的左边,
r[l[i]] = r[i];
l[r[i]] = l[i];
}
int main()
{
cin >> n >> k;
r[0] = 1; l[n + 1] = n;
priority_queue<PLL, vector<PLL>, greater<PLL>> q;
for (int i = 1; i <= n; ++ i)
{
//正好缺一个r[0] = 1; l[n + 1] = n;
l[i] = i - 1; r[i] = i + 1;
cin >> a[i];
q.push({a[i], i});
}
while (k -- )
{
PLL u = q.top();
q.pop();
auto [v, vi] = u;
if (v != a[vi])
{
q.push({a[vi], vi});
k ++ ;
}
else del(vi);
}
for (int i = r[0]; i != n + 1; i = r[i])
{
cout << a[i] << " ";
}
return 0;
}
砍树(LCA + 树上差分)
树上差分就是要用到LCA的板子
本题题解
树上边差分博客
树上点差分博客
注意点差分和边差分的区别;
具有抽象思维: 联通块类的题目每个点连接父节点的那条边的贡献度是1
数据范围又开小了,导致一直是前6个点能过 后面十几个点过不了(看到这里前几个能过后面过不了就应该想到是数据范围开小了的)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 5e5 + 10;
int h[N], e[N], ne[N], idx;
int s[N], ans = -1;
int d[N], f[N][21];
int n, m;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int u, int fa)
{
if(d[u]) return;
//预处理d[]
d[u] = d[fa] + 1;
f[u][0] = fa;
//预处理f[]
for (int k = 1; k <= 20; ++k)
{
f[u][k] = f[f[u][k - 1]][k - 1];
}
for (int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
dfs(v, u);
}
}
int lca(int a, int b)
{
if (d[a] < d[b]) swap(a, b);
for (int k = 20; k >= 0; -- k)
{
if (d[f[a][k]] >= d[b]) a = f[a][k];
}
if (a == b) return a;
for (int k = 20; k >= 0; -- k)
{
if (f[a][k] == f[b][k]) continue;
a = f[a][k], b = f[b][k];
}
return f[a][0];
}
void update(int u, int fa, int id, int &ans)
{
for (int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if (v == fa) continue;
update(v, u, i, ans);
s[u] += s[v];
}
if (s[u] == m)
{
ans = max(ans, id / 2 + 1);
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
for (int i = 0; i < n - 1; ++i)
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
//在lca前!!预处理d[]和f[];
dfs(1, 0);
for (int i = 0; i < m; ++ i)
{
int x, y;
cin >> x >> y;
s[x] ++, s[y] ++, s[lca(x, y)] -= 2;
}
//根节点为自定义的哨兵0, f[1] = 0;
//一个图从哪里开始遍历都行, 哪里拎起来都是一棵树
update(1, 0, 0, ans);
cout << ans;
return 0;
}