大家蓝桥都做完了吗!!!5+5变2+8梯度就上来了,感觉含金量会好一点(线上当我没说)
A. 进制转换
答案1478
解析略
B. 顺子日期
题目有争议,有人认为 012 不算顺子,有人认为 012 算。我个人倾向于 012 算顺子,因此答案为 14。以下是暴力代码:
(倒着的顺子肯定就不算了吧)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int ans;
signed main()
{
for (int i = 1; i <= 12; i++)
{
for (int j = 1; j <= days[i]; j++)
{
string s = "2022" + (to_string(i).size() == 2 ? to_string(i) : "0" + to_string(i)) + (to_string(j).size() == 2 ? to_string(j) : "0" + to_string(j));
for (int i = 0; i < 6; i++)
{
if (s[i] + 1 == s[i + 1] && s[i] + 2 == s[i + 2])
{
ans++;
break;
}
}
}
}
cout << ans << endl;
return 0;
}
C. 刷题统计
每 7 天为一个周期。我们可以先算出一个周期做多少题,再算出他做了几个周期的题。最后多余出来小于一个周期的部分(最多 6 天)暴力求解。
#include <bits/stdc++.h>
using namespace std;
int main(){
long long a, b, n;
cin >> a >> b >> n;
long long ans = n / (a * 5 + b * 2); // 算出有多少个完整周期
long long sum = ans * (a * 5 + b * 2); // 完整周期下做了多少题目
ans *= 7;
long long need = n - sum;
if(sum == n){
cout << ans << endl;
return 0;
}
for(int i = 1; i <= 5; i++){ // 算一下多余的部分是周一~周五的哪天完成
sum += a;
ans++;
if(sum >= n){
cout << ans << endl;
return 0;
}
}
cout << ans + 1 << endl; // 一定会在周六完成,如果周日完成的话就是一个完整周期了
return 0;
}
D. 修剪灌木
考虑一个灌木最多长多高。假设从左往右修剪,那么当剪到第 个灌木的时候,继续修剪到 ,再回到 修剪,这一过程就是这棵灌木的上限。除此之外还有第二种情况,即从右往左修剪,剪到位置 ,继续修剪到 1,再回到
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
cout << max(2 * (i-1), 2 * (n-i)) << endl;
}
return 0;
}
E. X 进制减法
需要首先理解数字中每一位的权值。例如十进制中 123 的 1 权值为 ,表示 ,而四进制中的 123 的 1 权值为 。
先将两个数字右对齐,然后从右边开始逐位进行减法操作。每一位的进制虽不相同,但是题目要求减法的结果最小,即两个数字尽量接近。位数一定时,如果进制大,则两个数字差距就大;如果进制小,则两个数字差距就小。所以我们应该让每一位的进制尽可能地小,但是又需要让数字合法,例如某一个数字是 5,那么这一位至少是六进制(5+1=6)。所以我们算出每一位的进制最小是多少,进行减法即可。
当每一位进制不同时,权值的计算方式是当前位右边所有位进制的乘积。例如个位是四进制,十位是六进制,那么百位的权值就是 。(此处的个十百位只是方便理解,表示的是从右往左数的第几位)
#include <iostream>
using namespace std;
const int mod = 1e9 + 7;
long long a[100001], b[100001];
int main(){
int n, m1, m2;
cin >> n >> m1;
for(int i = 1; i <= m1; i++){
cin >> a[i];
}
cin >> m2;
for(int i = m1-m2+1; i <= m2; i++){ // 这里为了右对齐,所以 i 的起始值需要注意一下
cin >> b[i];
}
long long val = 1, ans = 0;
for(int i = m1; i >= 1; i--){
int z = max(2, max(a[i], b[i]) + 1); // 算出最小进制,至少二进制,所以要求 max
ans += (a[i] - b[i]) * val;
ans %= mod;
val = (val * z) % mod; // val 表示当前位的权值
}
cout << ans << endl;
return 0;
}
F. 统计子矩阵
写了个 的做法,可能会 TLE 一部分。实际上用单调栈可以 。
首先使用二维前缀和,这样可以 O(1) 获得一个矩形的和。然后枚举矩形的左上角的 坐标,以及左下角的 坐标,二分右上角的 坐标(只需要 4 个坐标就可以唯一确定一个矩形)。由于矩形内部的和均为正数,所以固定三个坐标之后,另一个坐标越靠右,矩形的和越大,满足二分性。二分到最后一个满足条件的 坐标,说明前面的
#include <iostream>
using namespace std;
long long a[505][505], sum[505][505];
long long n, m, kk;
bool check(int i, int j, int k, int mid){
int now = sum[k][mid] - sum[i-1][mid] - sum[k][j-1] + sum[i-1][j-1];
if(now <= kk)
return true;
else
return false;
}
int main(){
cin >> n >> m >> kk;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> a[i][j];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j];
}
}
int res = 0;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
for(int k = i; k <= n; k++){
int l = j, r = m, ans=j-1;
while(l <= r){
int mid = (l + r) >> 1;
if(check(i,j,k,mid)){
l = mid + 1;
ans = mid;
}else{
r = mid - 1;
}
}
res += ans - j + 1; // ans 前面的都满足条件
}
}
}
cout << res << endl;
return 0;
}
G 积木画
前缀和优化计数 。 表示放好第 列的方案数。
首先,当铺满了前 的画布时第 块布只能摆放一个 型积木,故 对 的贡献为 ;而当铺满了前 的画布时可以竖放两个 型积木或横放两个 型积木,但竖放两个 型积木就会重复计 算一次 ,故 对 的贡献为 ;再来考虑其他情况:显然 型积木可以使用的数量只能为偶数,且第 为偶
数) 个 型积木与第 个之间一定是相匹配的,通过画图可以发现两个相匹配的 型积木之间只能摆放 个横向 型积木 与两个 型 积木之间的距离有关),而两个相匹配的 型积木只有两种摆法,因此可以得出递推式: , 用前缀和可以优化到 注意:两个
#include <iostream>
using namespace std;
const int maxn = 1e7 + 5;
const int mod = 1e9 + 7;
#define ll long long
ll dp[maxn],sum[maxn];
int main(){
int n;
cin >> n;
dp[0] = 1;
sum[0] = 1;
for(int i = 1; i <= n; i++){
dp[i] = (dp[i] + dp[i-1] + dp[i-2]) % mod;
if(i >= 3){
dp[i] = (dp[i] + 2 * sum[i-3]) % mod;
}
sum[i] = (sum[i-1] + dp[i]) % mod;
}
cout << dp[n];
return 0;
}
H 扫雷
dfs
待补
I 李白喝酒加强版
计数 ,注意如果李白的酒翻倍到超过
所以我们无需记录超过 的情况。dp[i][j][k]
表示走到 位置,还有 的酒,已经遇到过
每次只有两种转移,翻倍或者喝一次。
#include <iostream>
using namespace std;
const int maxn = 1e7 + 5;
const int mod = 1e9 + 7;
#define ll long long
ll dp[201][201][201];
int main(){
dp[0][2][0] = 1;
int n, m;
cin >> n >> m;
n += m;
for(int i = 0; i <= n-1; i++){
for(int j = 0; j <= n; j++){
for(int k = 0; k <= m; k++){
if(j > n - i) continue; // 重点,当酒的数量过多根本消耗不完的时候,不再转移
if(j != 0) // 遇到花
(dp[i+1][j-1][k+1] += dp[i][j][k]) %= mod;
if(j * 2 >= n || i == n-1){ // 最后一次必须是花,所以 i==n-1 时不进行后续转移
continue;
}
(dp[i+1][j*2][k] += dp[i][j][k]) %= mod; // 翻倍
}
}
}
cout << dp[n][0][m];
return 0;
}
J 砍竹子
待补
ps:Java Python组的最近有空就会出了,主要是不一样的题太多了家人们,而且感觉A组好难,想思考或者会的人可以直接转B站找wls:(包含A组B组C组c++的)https://www.bilibili.com/video/BV1tS4y1P7wD wlsyyds!!!