• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jacklee404
Never Stop!
博客园    首页    新随笔    联系   管理    订阅  订阅
2022蓝桥杯题解

2022蓝桥杯题解

九进制转十进制

\[(2022)_9 = (2 \times 9^0 + 2 \times 9^1 + 2 \times 9^3)_{10} \]

#include <iostream>
using i64 = long long;
int main() {
	int n, ans = 0, t = 1;
	std::cin >> n;
	while(n) {
		ans += (n % 10) * t;
		n /= 10;
		t *= 9;
	}
	std::cout << ans;
}

顺子日期

#include <iostream>

using i64 = long long;

int day[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

bool check(i64 date) {
	std::string t = std::to_string(date);
	int sz = t.size();
	for(int i = 2; i < sz; i ++) {
		if(t[i] - t[i - 1] == 1 && t[i - 1] - t[i - 2] == 1) {
			return true;
		}	
	}
	return false;
}

int main() {
	// 测试闰年, 非闰年
	// std::cout << ((2022 % 4 == 0 && 2022 % 100 != 0) || (2022 % 400) == 0);
	int ans = 0;
	for(int i = 1; i <= 12; i ++) {
		for(int j = 1; j <= day[i]; j ++) {
			i64 date = 2022 * 10000 + i * 100 + j;
			if(check(date)) {
				// std::cout << date << "\n";
				ans ++;
			}
		}
	}	
	std::cout << ans << "\n";
}

刷题统计

​ 先取模在判断

#include <iostream>

using i64 = long long;

int main() {
	i64 a, b, n;
	std::cin >> a >> b >> n;
	i64 k = n / (5 * a + 2 * b) * 7, t = n % (5 * a + 2 * b);
	// std::cout << t << "\n";
	if(t) {
		if(t <= 5 * a) {
			if(t % a == 0) {
				k += (t / a);
			} else {
				k += (t / a) + 1;
			}
		} else {
			k += 5;
			t -= 5 * a;
			if(t % b == 0) {
				k += (t / b);
			} else {
				k += (t / b) + 1;
			}
		}
	}
	std::cout << k;
}

修剪灌木

​ 对于第\(i\)棵灌木,其高度等于其左侧灌木对\(i\)的贡献和右侧灌木对\(i\)的贡献,可得

\[ans = max(2 \times (i - 1), 2 \times (n - i)) \]

#include <iostream>

using i64 = long long;

int main() {
	i64 n;
	std::cin >> n;
	for(int i = 1; i <= n; i ++) {
		std::cout << std::max((i64)2 * (i - 1), 2 * (n - i)) << "\n";
	}
}

X进制减法

​ 第一次做的时候进制怎么处理的都没看懂,其实对于普通的\(n\)进制,第\(i\)位系数是\(n^{i - 1}\), 对于进制不同的也一样,是前面的进制位连乘的形式, 另外处理\(a \bmod b\), \((321)_2 = 1 + 2 \times 2 + 3 \times 2 \times 10 = (65)_{10}\)

​ 知道这些,因为题目保证\(A > B\), 最低进制位为\(2\)进制,最高进制位为\(k\), 我们可以得到使\(A - B\)最小,则保证进制位最低,则第i位的进制\(dec_i = min(max(a_i, b_i) + 1, 2)\)

\[A = \sum \limits_{i = 1}^n (\prod \limits_{k = 1}^{i - 1}dec_k \times a_i) \\ B = \sum \limits_{i = 1}^n (\prod \limits_{k = 1}^{i - 1}dec_k \times b_i) \\ A - B = \sum \limits_{i = 1}^n (\prod \limits_{k = 1}^{i = 1}dec_k \times (a_i - b_i)) \]

模拟就完了,很简单 (注意洛谷上的测试集有点问题,需要+mod在模mod)
#include <iostream>
#include <vector>
#define sz(x) x.size()
using i64 = long long;

const int N = 1e6 + 10, mod = 1e9 + 7;

int n, ma, mb;

int a[N], b[N];

int main() {
	std::cin >> n;

	std::cin >> ma;

	for(int i = ma; i >= 1; i --) {
		std::cin >> a[i];
	}

	std::cin >> mb;

	for(int i = mb; i >= 1; i --) {
		std::cin >> b[i];
	}

	i64 cur = 1, ans = 0;

	for(int i = 1; i <= std::max(ma, mb); i ++) {
		int dec = std::max(std::max(a[i], b[i]) + 1, 2);

		ans = (ans + (a[i] - b[i]) * cur) % mod;

		cur = (cur * dec) % mod;
	}
    
	std::cout << ans % mod;
}

统计子矩阵

​ 可以通过枚举矩阵的宽\(h\), 然后对矩阵的列做双指针,通过二维前缀和可以\(O(1)\)算时间,如果当前矩阵小于\(k\), 则让\(j\)右移,区间长度\(j - i\)为贡献。

#include <iostream>

using i64 = long long;

const int N = 1e3 + 10;

int n, m, k;

int a[N][N];

i64 sum[N][N];

i64 calc(int x1, int y1, int x2, int y2) {
    // 保证两个指针始终满足y1 <= y2
	if (y1 > y2) {
		return -1;
	}
	i64 k = sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 - 1];
	return k;
}

int main() {
	std::cin >> n >> m >> k;
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= m; j ++) {
			std::cin >> a[i][j];

			sum[i][j] = a[i][j] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
		}
	}
	i64 ans = 0;
	for(int h = 1; h <= n; h ++) {
		for(int r = 1; r <= n; r ++) {
			int x1 = r, x2 = r + h - 1;
			if(x2 > n) break;
			for(int y1 = 1, y2 = 1; y1 <= m; y1 ++) {
				while(calc(x1, y1, x2, y2) <= k  && y2 <= m) y2 ++;		
				ans += (y2 - y1);
			}
		}
	}

	std::cout << ans;
}

积木画

​ 考虑\(f[i][state]\) 为前\(i\)列已经摆放整齐,第\(i\)列状态为\(state\),则\(state\)可以分为\(0\), \(1\), \(2\), \(3\), 分别表示该列没有方格,第上方一个方格,第下方一个方格,两个方格。

则状态转移为:

\[f[i][0] = f[i - 1][0] \\ f[i][1] = f[i - 1][0] + f[i - 1][2] \\ f[i][2] = f[i - 1][0] + f[i - 1][1] \\ f[i][3] = f[i - 1][1] + f[i - 1][2] + f[i - 1][0] \\ \]

其中\(f[i][3]\)可以从\(f[i - 1][0]\) , 为以下状态

image-20230322194709620

#include <iostream>

using i64 = long long;

const int mod = 1e9 + 7, N = 1e7 + 10;
/*
	f[i][j] 表示第i列的状态为j时的总方案数 (并且前i - 1列已经摆好)

	f[i][0] 表示第i列没有方格

	f[i][1] 表示第i列为上方方格一个

	f[i][2] 表示第i列为下方方格一个

	f[i][3] 表示第i列全方格

	f[i][0] = f[i - 1][3];

	f[i][1] = f[i - 1][0] + f[i - 1][2];

	f[i][2] = f[i - 1][0] + f[i - 1][1];

	f[i][3] = f[i - 1][0] + f[i - 1][1] + f[i - 1][2] + f[i - 1][3];
*/

int f[5][5];

int main() {
	int n;

	std::cin >> n;

	f[0][3] = 1;

	for(int i = 1; i <= n; i ++) {
		f[i & 1][0] = f[i - 1 & 1][3];

		f[i & 1][1] = ((i64)f[i - 1 & 1][0] + f[i - 1 & 1][2]) % mod;

		f[i & 1][2] = ((i64)f[i - 1 & 1][0] + f[i - 1 & 1][1]) % mod;

		f[i & 1][3] = (((i64)f[i - 1 & 1][0] + f[i - 1 & 1][1]) % mod + ((i64)f[i - 1 & 1][2] + f[i - 1 & 1][3])) % mod;
	}

	std::cout << f[n & 1][3];
}

扫雷

​ 该题粗略的看一下复杂度达到了\(O(n \times 10 \times 10 + m)\), 其中\(n\)为所有的雷, \(10 \times 10\)是我们枚举圆心周围的方格,当\(n\)个圆心在一个点时候,边的复杂度会\(O(n \times n)\), 所以我们可以去掉相同的点有多个圆心,只保留最大的半径。

​ 另外该题的复杂度接近于\(1e7\), 我们应该使用unordered_map来存储点,这时就需要用到坐标轴哈希,\(x \times (maxy + 1) + y\),具体做法就是用哈希来寸结构体下标, 结构体内存储该点的雷的数量和最大半径,然后对于每一个引爆雷跑一下连通块。

#include <iostream>
#include <unordered_map>
#include <vector>

using i64 = long long;

struct node {
	int x, y, r, l;
} a[500010];

int n, m, cnt, ans = 0;

std::unordered_map<i64, int> idx;

bool b[500010];

i64 calc(i64 x, i64 y) {
	return 1LL * x * (1e9 + 1) + y;
}

inline void dfs(int u) {
	b[u] = true;
	ans += a[u].l;

	for(i64 x = a[u].x - a[u].r; x <= a[u].x + a[u].r; x++) {
		i64 d = 1LL * (x - a[u].x) * (x - a[u].x);
		
		for(i64 y = a[u].y; 1LL * (y - a[u].y) * (y - a[u].y) + d <= 1LL * a[u].r * a[u].r && y <= 1e9; y++) {
			if(idx.find(calc(x, y)) != idx.end()) {
				int k = idx[calc(x, y)];
				if(!b[k])
					dfs(k);
			}
		}

		for(i64 y = a[u].y; 1LL * (y - a[u].y) * (y - a[u].y) + d <= 1LL * a[u].r * a[u].r && y >= 0; y--) {
			if(idx.find(calc(x, y)) != idx.end()) {
				int k = idx[calc(x, y)];
				if(!b[k])
					dfs(k);
			}		
		}
	}
}

int main() {
	std::cin >> n >> m;

	for (int i = 1; i <= n; i++) {
		int x, y, r;

		std::cin >> x >> y >> r;

		if(idx.find(calc(x, y)) == idx.end()) {
			idx[calc(x, y)] = ++ cnt;
			a[cnt].x = x, a[cnt].y = y, a[cnt].r = r;
		}

		int s = idx[calc(x, y)];

		a[s].r = std::max(a[s].r, r);
		
		a[s].l ++;
	}
	
	for (int i = 1; i <= m; i ++) {
		
		std::cin >> a[cnt + 1].x >> a[cnt + 1].y >> a[cnt + 1].r;

		a[cnt + 1].l = 0;

		dfs(cnt + 1);
	}

	std::cout << ans;
}

参考dls代码

砍竹子

​ 一开始用单调队列写的,但是卡常了,有几个样例过不去。 正解是先把第一个砍到1,然后记录历史值,枚举第\(i\)个的时候查询是否与\(i - 1\)的历史值相同, 只记录有效操作。 复杂度\(O(n)\)

#include <iostream>
#include <cmath>
#include <vector>
#define act(x) sqrt(x / 2 + 1)
using i64 = long long;

const int N = 2e5 + 10;

i64 a[N], ans = 0;

std::vector<i64> g[N];

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int n;

    std::cin >> n;

    for (int i = 1; i <= n; i++) std::cin >> a[i];

    for (int i = 1; i <= n; i++) {
        while (a[i] > 1) {
            bool ok = false;
            for (i64 t: g[i - 1]) {
                if (t == a[i]) {
                    ok = true;
                    break;
                }
            } 
            if (!ok) ans ++;
            g[i].push_back(a[i]);
            a[i] = act(a[i]); 
        }
    }

    std::cout << ans;
}

李白打酒

​ 考虑\(f[i][j][k]\) 表示经过了\(i\)家店,\(j\)朵花,酒的数量为\(k\), 则状态转移为

\[f[i][j][k] = f[i][j - 1][k + 1] + f[i - 1][j][k / 2] \]

​ 最后答案为\(f[n][m - 1][1]\), 因为题目要求最后一定是花

#include <iostream>
#include <algorithm>

using i64 = long long;

const int mod = 1e9 + 7, N = 200;

/* 
	f[i][j][k] 

	表示经过了i次店,j朵花, 当前酒量为k时的方案数

	f[i][j][k] =  f[i - 1][j][k + 1] + f[i][j - 1][k >> 1];
*/

int n, m;

int f[N][N][1010];

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);


	std::cin >> n >> m;

	f[0][0][2] = 1;

	for(int i = 0; i <= n; i ++) {
		for(int j = 0; j <= m; j ++) {
			for(int k = 0; k <= 1000; k ++) {
				if(j)	
					f[i][j][k] += f[i][j - 1][k + 1];

				if(i && k % 2 == 0) {
					f[i][j][k] = ((i64)f[i][j][k] + f[i - 1][j][k >> 1]) % mod;
				}
			}
		}
	}

	std::cout << f[n][m - 1][1];
}
posted on 2023-03-22 20:44  Jack404  阅读(23)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3