2025年蓝桥杯B组题解

A 感觉怪怪的神秘题

B 巨大思维检测题

C 找不是1的个数

D 暴力模拟,复杂度是对的

E 排序之后前缀和或者双指针

F 找前面出现的有1的位置分类讨论一下

G bitset的树形背包

H 幽默的智力检测题

A:984

B :179780307

对于任意 a i × a j ≤ i × j + 2025 a_i \times a_j \leq i \times j + 2025 ai×aji×j+2025
求方案数
答案对 1 0 9 + 7 10^9 + 7 109+7 取模 n < = 2025 n <= 2025 n<=2025

思考

考虑 2025 2025 2025 应该在哪
如果 2025 2025 2025 x x x 位置
那么 要求 x 2 + 2025 ≥ 2025 × 2025 x ^2 + 2025 \ge 2025 \times 2025 x2+20252025×2025
那么 x 2 ≥ 2024 × 2025 x^2 \ge 2024 \times 2025 x22024×2025
可以发现 x x x 必须等于 2025 2025 2025
反推
2024 2024 2024 应该在哪
假设在 y y y
那么 2025 × 2024 ≤ 2025 × y + 2025 2025 \times 2024 \le 2025 \times y + 2025 2025×20242025×y+2025
那么 2025 × 2024 ≤ 2025 × ( y + 1 ) 2025 \times 2024 \leq 2025 \times (y + 1) 2025×20242025×(y+1)
由于 y y y 不能等于 2025 2025 2025
所以可得 y = 2024 y = 2024 y=2024 或者 2023 2023 2023
同时有 2024 × 2024 ≤ y × y + 2025 2024 \times 2024 \le y \times y + 2025 2024×2024y×y+2025
由于 2023 × 2024 + 2 < 2024 ∗ 2024 2023 \times 2024 + 2 < 2024 * 2024 2023×2024+2<20242024
所以 y y y 只能在 2024 2024 2024
顺推
假设 2023 2023 2023 z z z 号位置
那么有式子
2025 × 2023 ≤ 2025 × z + 2025 2025 \times 2023 \le 2025 \times z + 2025 2025×20232025×z+2025
2024 × 2023 ≤ 2024 × z + 2025 2024 \times 2023 \le 2024 \times z + 2025 2024×20232024×z+2025
2025 × 2023 ≤ 2025 × ( z + 1 ) 2025 \times 2023 \le 2025 \times (z + 1) 2025×20232025×(z+1)
可以发现 z z z 只能选择 2023 2023 2023 或者 2022 2022 2022
数学归纳法

对于数字 x 在位置 y
2025 × x ≤ 2025 × y + 2025 2025 \times x \le 2025 \times y + 2025 2025×x2025×y+2025
2025 × x ≤ 2025 × ( y + 1 ) 2025 \times x \le 2025 \times (y + 1) 2025×x2025×(y+1)
即有
x ≤ ( y + 1 ) x \le (y + 1) x(y+1)
可以推导出 必须放在 x − 1 x - 1 x1之后
但上述推导可以发现对于后几位数字是存在一个问题的
即特别大的几个数字的位置是固定在某个位置的
仅当数字变小到达一定程度之后会导致 + 2025 + 2025 +2025 使得前面的位置可以发生变化
那么可以尝试枚举

推导一下

假设 1 1 1 号位在 1 1 1,或者 2 2 2
如果 1 1 1 2 2 2
2 2 2 要么在 1 1 1,要么在 3 3 3
如果 2 2 2 3 3 3,那么 3 3 3 无法放到 1 1 1
所以 2 2 2 必须放在 1 1 1
所以固定枚举顺序为
1   2 1\ 2 1 2 或者 2   1 2\ 1 2 1
考虑 3   4 3\ 4 3 4 应该也会存在这种情况
所以应该是一个 d p N dp_N dpN 的式子
来维护上面所设置的选择要求
考虑 d p dp dp 的后效性问题
假设数字 x x x 放在了 x x x
当前数字为 z z z 小于 x x x
那么有可能会有的最坏情况有两种
z × x ≤ z × ( x − 1 ) + 2025 z \times x \le z \times (x - 1) + 2025 z×xz×(x1)+2025
由于 z z z 一定小于 2025 2025 2025 ,所以一定合法
对于 z × x ≤ ( z − 1 ) × ( x − 1 ) + 2025 z \times x \le (z - 1) \times (x - 1) + 2025 z×x(z1)×(x1)+2025
可以发现
对于如果存在 z × x ≤ ( z − 1 ) × ( x − 1 ) + 2025 z \times x \le (z - 1) \times (x - 1) + 2025 z×x(z1)×(x1)+2025 的 x
一定会提前被 x × x ≤ ( x − 1 ) × ( x − 1 ) + 2025 x \times x \le (x - 1) \times (x - 1) + 2025 x×x(x1)×(x1)+2025 给拦截

所以 对于不满足上面条件的事情而言
他会固定位置
否则一定可以交换前面的
所以找到个数即可

#include<bits/stdc++.h>
using namespace std;
using LL = long long;
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout);
const int mod = 1e9 + 7;
int main(){
	cin.tie(nullptr) -> ios::sync_with_stdio(false);
	const int n = 2025;
	vector<LL> dp(n + 1);
	dp[0] = 1,dp[1] = 1;
	for(int i = 2;i <= 2025; i ++){
		if(i * i <= (i - 1) * (i - 1) + 2025)
			dp[i] = (dp[i - 1] + dp[i - 2]) % mod;
		else {
			dp[i] = dp[i - 1];
		}
	}
	cout << dp[n] <<"\n";
	return 0;
}

C

对于数字 x x x 满足 [ − ( x − 1 ) ] + [ − ( x − 2 ) ] + . . . + 0 + 1 + 2 + . . . + x = x [-(x-1)] + [-(x - 2)] +... + 0 + 1 + 2 +... +x = x [(x1)]+[(x2)]+...+0+1+2+...+x=x

所以对于稍微大一点的x均满足

对于数字 1 1 1 而言,他死了

对于数字 2 2 2 而言, − 1 + 0 + 1 + 2 -1 + 0 + 1 + 2 1+0+1+2 可以

所以特判 1 1 1 即可

#include<bits/stdc++.h>
using namespace std;
using LL = long long;
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout);
int main(){
	cin.tie(nullptr) -> ios::sync_with_stdio(false);
	int n,ans = 0;
	cin >> n;
	for(int i = 1;i <= n; i ++){
		int x;
		cin >> x;
		ans += (x != 1);
	}
	cout << ans <<"\n";
	return 0;
}

D

显然

要么会变成定值

要么 会变成几个数一直在那循环

比方说 1 2 2?

1 2 2

2 1 1

怎么好像不循环

试一下先

打表可得

// 表
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout);
int main(){
	cin.tie(nullptr) -> ios::sync_with_stdio(false);
	for(int i = 1;i <= 100;i ++){
		for(int j = 1;j <= 100;j ++){
			for(int k = 1; k <= 100;k ++){
				int a = i,b = j,c = k;
				int cnt = 0;
				while(true){
					cnt ++;
					int aa = b + c >> 1,bb = a + c >> 1,cc = a + b >> 1;
					if(a == aa && b == bb && c == cc)break;
					a = aa,b = bb,c = cc;
					if(cnt > 10){
						cout << i <<" "<<j <<" "<<k <<"\n";
						return 0;
					}
				}
			}
		}
	}
	return 0;
}

不存在次数多的

所以有

#include<bits/stdc++.h>
using namespace std;
using LL = long long;
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout);
int main(){
	cin.tie(nullptr) -> ios::sync_with_stdio(false);
	int T;
	cin >> T;
	while(T--){
		int a,b,c,k;
		cin >> a >> b >> c >> k;
		while(k --){
			int aa = b + c >> 1,bb = a + c >> 1,cc = a + b >> 1;
			if(a == aa && b == bb && c == cc)break;
			a = aa,b = bb,c = cc;
		}
		cout << a <<" "<< b <<" "<<c <<"\n";
	}
	return 0;
}

E

首先有 一定按顺序排

如果不按顺序一定会使得贡献变大

所以其实就是相邻的数做一下

#include<bits/stdc++.h>
using namespace std;
using LL = long long;
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout);
int main(){
	cin.tie(nullptr) -> ios::sync_with_stdio(false);
	int n,m;
	cin >> n >> m;
	vector<int> a(n + 1);
	for(int i = 1;i <= n ;i ++){
		cin >> a[i];
	}	
	sort(a.begin(),a.end());
	LL ans = 1e18;
	vector<LL> b(n);
	for(int i = 1;i < n; i ++){
		b[i] = 1LL * a[i + 1] * a[i + 1] - 1LL * a[i] * a[i];  
		b[i] += b[i - 1];
	}
	for(int i = m - 1;i < n ;i ++){
		ans = min(ans,b[i] - b[i - m + 1]);
	}
	cout << ans <<"\n";
	return 0;
}

F

差一点以为斯坦树板子了

2 × n 2 \times n 2×n 的河床

所以直接大力跑 d p dp dp 连接前面是对的

那么前面有几种情况

前面如果是和我同一行,那么我就直接连

否则把我变成一列全是1的形态

然后去做连接

所以简单离散化一下然后直接跑

#include<bits/stdc++.h>
using namespace std;
using LL = long long;
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout);
int main(){
	// fre("");
	cin.tie(nullptr) -> ios::sync_with_stdio(false);
	string a,b;
	cin >> a >> b;
	vector<pair<int,int>> v;
	for(int i = 0; i < a.size();i ++){
		int cnt = (a[i] == '#')  * 2 + (b[i] == '#');
		if(cnt){
			v.push_back({cnt,i});
		}
	}
	int ans = 0;
	for(int i = 1;i < v.size();i ++){
		if(v[i].first & v[i - 1].first){
			ans += v[i].second - v[i - 1].second - 1;
		}else {
			v[i].first |= 3;
			ans += v[i].second - v[i - 1].second;
		}
	}
	cout << ans <<"\n";
	return 0;
}

G

树上背包…

朴素的是 n 3 n^3 n3 的,然后可以考虑 b i t s e t bitset bitset 加速一下,复杂度会优化成 n 3 w \frac{n^3}{w} wn3,其中 w = 64 w = 64 w=64

当然也可以手撕一个多项式卷积,可以将复杂度优化到 n 2 log ⁡ 2 n n^2\log_2n n2log2n

#include<bits/stdc++.h>
using namespace std;
using LL = long long;
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout);
const int N = 1e3 + 10;
int a[N];
vector<int> node[N];
bitset<N> dp[N],temp;
void dfs(int u,int fa){
	dp[u][0] = 1;
	int son = 0;
	for(int x:node[u])if(x != fa){
		dfs(x,u);
		son ++;
		temp = dp[u];
        // 这个位置可以写成f * g 的多项式卷积,掏任意一个卷积模板都可以使得他优化成nlog
		for(int i = 1;i <= a[x];i ++){
			if(dp[x][i]){
				dp[u] |= temp << i;
			}
		}
	}
	if(son == 0)dp[u][a[u]] = 1;
	for(int i = a[u] + 1;i < N ;i ++){
		dp[u][i] = 0;
	}
}
int main(){
	cin.tie(nullptr) -> ios::sync_with_stdio(false);
	int n;
	cin >> n;
	for(int i = 1;i <= n ;i ++){
		cin >> a[i];
	}
	for(int i = 1;i < n ;i ++){
		int x,y;
		cin >> x >> y;
		node[x].push_back(y);
		node[y].push_back(x);
	}
	dfs(1,0);
	int ans = 0;
	for(int i = 1;i < N ;i ++){
		if(dp[1][i]){
			ans = i;
		}
	}
	cout << ans <<"\n";
	return 0;
}

H

需要特殊处理一下 前缀

枚举段,然后枚举前面的符号,去计算贡献?

怎么前面一加,后面一减就抵消了

所以只有前缀需要算

那么对于前缀从一个位置断开之后,后面计算出现次数即可

#include<bits/stdc++.h>
using namespace std;
using LL = long long;
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout);
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
LL p3[N];
int main(){
	cin.tie(nullptr) -> ios::sync_with_stdio(false);
	int n;
	cin >> n;
	p3[0] = 1;;
	for(int i = 1;i <= n ;i ++){
		p3[i] = p3[i - 1] * 3 % mod;
	}
	LL ans = 0,sum = 0;
	for(int i = 1;i <= n ;i ++){
		int x;
		cin >> x;
		sum ^= x;
		// 后面一个符号一定要选择 + 或者 -,除非是最后一个
		// 其他可以选3种
		if(i != n){
			ans += (sum * 2) * p3[n - i - 1] % mod;
		}else {
			ans += sum;
		}
		ans %= mod;
	}
	cout << ans <<"\n";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值