2026第十七届蓝桥杯c++b组省赛
2026第十七届蓝桥杯c++b组省赛
A-青春常数
[P16232 蓝桥杯 2026 省 B] 青春常数 - 洛谷

第一题很明显的就是除以二再加一,因为0<=x然后x<y所以当时直接用计算器算的直接就是得到答案。
#include<bits/stdc++.h>
using namespace std;
signed main(){
cout<<"1013101260121012";
return 0;
}
B-双碳战略
[P16233 蓝桥杯 2026 省 B] 双碳战略 - 洛谷

题目大意
有 2026 盏排成一条直线的智能路灯,初始全部亮着。
总共有 22026 种状态,每种状态都能被操作到达。
对于执行的操作:
- 第奇数次操作:选一盏灯 i,翻转灯 i 和它右边所有灯。
- 第偶数次操作:选一盏灯 i,翻转灯 i 和它左边所有灯。
我们需要求出对每一种状态,算出从初始全亮到达它的最少操作次数,把所有状态的最少次数加起来,结果对 998244353 取模输出。
这个题我也不知道怎么做我看up主的打表最后得出结论
打表这个思路可以,主要去学习思路
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
const int N=2e5+10,M=1e3+10;
int a[N],b[N];
int n;
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
vector<int>s(n);//00000000
queue<vector<int>>q;//队列用来存储状态
map<vector<int>,int>d;
q.push(s);
d[s]=0;//状态
while(q.size()){
auto u=q.front();
q.pop();
for(int i=0;i<n;i++){//枚举每个位置
auto v=u;
if(d[u]%2==0){
for(int j=i;j<n;j++){
v[j]^=1;
}
}else{
for(int j=0;j<=i;j++){
v[j]^=1;
}
}
if(!d.count(v)){
q.push(v);
d[v]=d[u]+1;
}
}
}
int ans=0;
for(auto & [v,dist]: d){
for(auto & x:v){
cout<<x<<" ";
}
cout<<endl;
cout<<"步数: "<<dist<<endl;
ans+=dist;
}
cout<<ans<<endl;
return 0;
}
然后我从中总结出来,每个位置的贡献
总和 = n * 2^(n-1)
所以之后只要求这个贡献就行了
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
register int n = 2026, ans = 1;
for (register int i = 1; i < n; ++i)
ans <<= 1, ans %= 998244353;
ans *= n, ans %= 998244353;
cout << ans;
return 0;
} // ans = 792264670
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MOD = 998244353;
int qpow(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
signed main() {
int n = 2026;
int ans = n * qpow(2, n - 1) % MOD;
cout << ans << endl;
return 0;
}
C-循环右移
[P16234 蓝桥杯 2026 省 B] 循环右移 - 洛谷

循环右移的本质就是把最后一个数字移到第一个数字的位置,对某个数组,想要不管怎么进行循环右移都与原来相同,那么就要要求每个数字都相同,也就是说方案数就是范围内数字的个数,注意判断范围错误的情况。
输入 #1
3
3 1 2
5 10 10
2 5 3
输出 #1
2
1
0
题解
这个题很好理解,移动后相同只有一种情况,就是全部相等的情况,所以我们只要判断x,y相差多少
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
const int N=2e5+10,M=1e3+10;
int a[N],b[N];
int n;
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--){
int n,x,y;
cin>>n>>x>>y;
if(x>y){
cout<<0<<endl;
}else{
cout<<y-x+1<<endl;
}
}
return 0;
}
D-蓝桥竞技
[P16235 蓝桥杯 2026 省 B] 蓝桥竞技 - 洛谷

输入 #1
4
5
1 1 1 1 1
6
2 2 2 2 1 1
5
1 1 1 1 2
6
3 1 1 1 2 2
输出 #1
T
T
F
F
题解
很明显这个题,可以用排除的方法,先把不行的排除,有两个,一个总数不是5的倍数,一个平均分下来的数里面还有数比他大的
设总人数 S=∑A**i,战队数 k=S/5。
- 首先 S 必须是 5 的倍数。
- 每种位置最多出现在 k 支战队里,所以每个 A**i≤k。
这两个条件加起来就是充要条件。
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
const int N=2e5+10,M=1e3+10;
int a[N],b[N];
int n;
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--){
cin>>n;
int s=0,m=0;
for(int i=1;i<=n;i++){
cin>>a[i];
s+=a[i];
m=max(m,a[i]);
}
if(s%5)cout<<"F";
else cout<<(m<=s/5?"T":"F");
cout<<endl;
}
return 0;
}
E-LQ 聚合
[P16236 蓝桥杯 2026 省 B] LQ 聚合 - 洛谷

输入 #1
5
??L??
输出 #1
6
解题
LQ聚合”的规则。不难发现这个题的本质就是在求对于每个 L 的后面的 Q 个数之和。而对于每个可以修改的 ? 的位置。将其从 Q 改为 L 的代价为当前的和 *sum* 减去 i 前面 L 的数量加上 i 后面 Q 的数量。因为对于前面的 L,少了一个 Q 相当于就少了一部分的 *LQ*。但对于后面的 Q 来说,这反而有创造出了一些 L**Q。如果说你创造的 L**Q 比减少的多,说明这个地方可以改为 L。否则不改。
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
const int N=2e5+10,M=1e3+10;
int a[N],b[N];
int n;
int bck[N],vis[N];
int cntl,cntq;
int sum=0;
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
string s;
cin>>n>>s;
s=" "+s;
for(int i=1;i<=n;i++){
if(s[i]=='?')s[i]='Q',vis[i]=1;
}
for(int i=n;i>=1;i--){//处理后缀Q的个数。
bck[i]=bck[i+1]+(s[i]=='Q');
}
//统计现在的价值
for(int i=1;i<=n;i++){
if(s[i]=='L'){
sum+=bck[i];
}
}
cntq=bck[1];//初始后缀Q数量
cntl=0; //初始前缀L数量
for(int i=1;i<=n;i++){
if(s[i]=='L')cntl++;//是 L,前缀 L 数量++。
if(s[i]=='Q')cntq--;// 是 Q,后缀 Q 数量--。
if(vis[i]){
int x=sum;
if(x<sum-cntl+cntq){
sum=sum-cntl+cntq,cntl++;
}
}
}
cout<<sum;
return 0;
}
二进制做法
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
string s;
cin >> n >> s;
// 收集所有 ? 的位置
vector<int> ques;
for (int i = 0; i < n; i++) {
if (s[i] == '?') ques.push_back(i);
}
int m = ques.size();
long long ans = 0;
// 枚举 2^m 种状态
for (int mask = 0; mask < (1 << m); mask++) {
string t = s;
// 根据 mask 填充 ?
for (int i = 0; i < m; i++) {
if (mask >> i & 1) t[ques[i]] = 'Q';
else t[ques[i]] = 'L';
}
// 计算 LQ 聚合数量
long long cntL = 0, res = 0;
for (int i = 0; i < n; i++) {
if (t[i] == 'L') cntL++;
else if (t[i] == 'Q') res += cntL;
}
ans = max(ans, res);
}
cout << ans << endl;
return 0;
}
dfs
#include <bits/stdc++.h>
using namespace std;
int n;
string s;
vector<int> ques;
long long ans = 0;
// 计算当前字符串的 LQ 聚合数
long long calc(const string& t) {
long long cntL = 0, res = 0;
for (char c : t) {
if (c == 'L') cntL++;
else if (c == 'Q') res += cntL;
}
return res;
}
// idx: 当前处理到第几个 ?
void dfs(int idx, string& t) {
if (idx == ques.size()) {
ans = max(ans, calc(t));
return;
}
int pos = ques[idx];
// 选 L
t[pos] = 'L';
dfs(idx + 1, t);
// 选 Q
t[pos] = 'Q';
dfs(idx + 1, t);
// 恢复(可选,因为直接覆盖了)
t[pos] = '?';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> s;
for (int i = 0; i < n; i++) {
if (s[i] == '?') ques.push_back(i);
}
string t = s;
dfs(0, t);
cout << ans << endl;
return 0;
}
、
F-应急布线
[P16237 蓝桥杯 2026 省 B] 应急布线 - 洛谷

输入 #1
7 2
1 2
2 3
输出 #1
4 2
题解
一开始会形成若干连通块,于是题目就是让你求把若干连通块变成一个的最小花费。那么答案一定是连通块数减一,第一问就做完了。
设第一问答案为 cnt。
注意到每次连边会将两个机子连上,共要连 cnt 次。那么 n 台机子共会连上 2cnt 次。最好情况就是均摊。于是第二问答案为 ⌈n2cnt⌉。
#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
const int N=2e5+10,M=1e3+10;
int n,m,cnt;int fa[N];
int find(int x){//并查集标准查找 + 路径压缩:
return fa[x]==x?x:fa[x]=find(fa[x]);
}
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
fa[i]=i;
}
while(m--){
int a,b;
cin>>a>>b;
fa[find(a)]=find(b);
}
for(int i=1;i<=n;i++){
if(find(i)==i)cnt++;//查找根
}
cout<<--cnt<<' '<<ceil(2.0*cnt/n);
return 0;
}
G-理想温度
[P16238 蓝桥杯 2026 省 B] 理想温度 - 洛谷

输入 #1
5
1 2 3 4 5
2 3 2 3 2
输出 #1
2
给定表示现在状态的数组 a 和表示现在目标的数组 b。可以选择一段连续的区间 [l,r] 和一个整数 k,令区间内所有 a**i 加上 k。问操作后最多能使多少个 i 满足 a**i=b**i。
这个我只能理解暴力做法,不能理解另一个做法只能这样了。
题解
我们只要枚举l和r,在这个区间找出现最多的数,当然一开始还有要预处理一下这个的,然后就可以;

#include<bits/stdc++.h>
#define int long long
#define lll __uint128_t
#define PII pair<int ,int>
#define endl '\n'
using namespace std;
const int N=2e5+10,M=1e3+10;
int n,m,cnt;int fa[N];
int ans=0;
signed main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1,x;i<=n;i++){
cin>>x;
a[i]-=x;
}
int ans=0;
vector<int>pre(n+1),suf(n+2);
for(int i=1;i<=n;i++){//处理前缀0的个数
pre[i]=pre[i-1]+(a[i]==0);
}
for(int i=n;i>0;i--){
suf[i]=suf[i+1]+(a[i]==0);
}
for(int l=1;l<=n;l++){
map<int,int>mp;
int mx=0;
for(int r=l;r<=n;r++){
//从[l,r]中选择一个众数
mp[a[r]]++;
mx=max(mx,mp[a[r]]);
ans=max(ans,mx+pre[l-1]+suf[r+1]);
}
}
cout<<ans<<endl;
return 0;
}

H-足球训练
[P16239 蓝桥杯 2026 省 B] 足球训练 - 洛谷

输入 #1
2 3
4 2
5 3
输出 #1
66
这个题还是不懂,当时把大佬苯环的代码cp下来了
题解
首先我们要先确定出给谁操作才是最优的,直接手玩样例是不简单的,其实我们可以用 n=2 的小数据手玩。
我们可以先令 m=1,看能不能发现什么规律。
例如 {x=1,y=3},{x=1,y=100},那我们肯定是操作后者,把第二个 pair 变成 {x=101,y=100}。
这样看上去似乎是操作 x 比较大的。但当第二个 pair 换成 {x=100,y=100} 的时候,此时我们发现操作第一个反倒更好。
如果你看懂了这个简单的例子,你就会发现我们每次操作的那个 pair,不一定非得要增量 y 是最大的,而应该让增幅最大。
让我们用下面这个更一般化的例子看一看:
现在有 n 个 pair,现在我们要决策当前这次操作给 i=1 还是 i=2。也就是说,原本的乘积是:某定值 S×x1×x2(其中 S 表示其余 n−2 个 x 的乘积),现在我们让 x1 变成 x1+y1,贡献变成了:
(x1+y1)×x2×S
如果我们给 x2 操作,那么贡献会反过来变成:
(x2+y2)×x1×S
假设最终给 x1 操作更好,也就是说:(x1+y1)×x2×S>(x2+y2)×x1×S 的话,我们推导一下。
两边的 S 显然可以直接约掉,然后把左边的 x2 除到右边,右边的 x1 除到左边,式子变成了:
x1x1+y1>x2x2+y2
显然,化简后变成了:
x1y1>x2y2
也就是说:我们一定是把操作给到 xy 最大的那个 pair 上。
于是我们简单得到了 60 分做法,我们用优先队列存储所有的 pair,每次操作最优的进行贪心,维护时按照 x**y 从大到小,因此用大根堆。(当然,这里为了避免精度问题,最好是把除法变成乘法)
但 100 分怎么拿呢?这其实是非常经典的 trick,我们来介绍一下。
首先为了后面好处理,我们先把排序的规则改成按照 y**x 从小到大(这和按 x**y 从大到小本质一样)。
此时我们会发现,操作一定会选择 y**x 最小的那个进行,于是:在最优的 m 次操作过程中,整个序列中 n 个 pair 里的 yx 的最小值一定是单调不降的。因为我们每次选的是最小的那个操作,然后操作它之后,他的 yx 就会增大。
于是,我们就可以考虑直接二分所有操作结束后的:min(y**x) 的最大值。(二分最小值的最大值)
这样一来,对于某个 pair,如果其 y**x 值小于我们二分出来的 mi**d,则我们就要不停操作这个 pair,直到它的值 ≥mi**d,这样一来,对于每个 pair,我们就可以在 O(1) 的时间内把它应做的所有操作都做完。(简单推个式子即可)
于是我们以任意顺序处理完所有 n 个 pair 即可,但这里又出现了新的问题,实际上有可能在所有 pair 都操作到不小于 mi**d 后,m 还可能有剩下的,这个问题出现的原因就好比:{2,2,2} 和 {2,3,3} 这两个数组的最小值都是 2 一样。
但这时,我们一定可以证明的是,此时的 m 在给所有 pair 都操作到其 y**x≥m 后,m 一定严格小于 n。
这是因为,如果 m≥n,那就意味着:我们至少可以把 n 个 pair 每人都再操作一次,于是当前二分出来的 mi**d 一定不是最优解对应的 mi**d。(最优解的 mi**d 肯定能更大)
因此只要我们二分出来的 mid 就是最优解对应的 mid,则 m<n 一定成立。
而我们发现此时 m 其实已经非常小了,我们直接无脑套用 60 分做法即可。(即优先队列 O(m×logm) 模拟)
详见代码:(实现上有些细节,例如我们二分的本质上是浮点数,于是可以直接暴力二分 100 次,这样能避免精度问题可能产生的死循环问题。)
struct Node {
int x, y, id;
bool operator<(const Node & n) const {
int nx = n.x, ny = n.y;
// nx / ny < x / y;
return nx * y < x * ny;
}
};
void solve() {
int n, m;
cin >> n >> m;
vector<int> a(n + 1), b(n + 1);
for(int i = 1; i <= n; i++) {
cin >> a[i] >> b[i];
}
auto check = [&](double mid) {
int C = 0; // 要使得所有 pair 的 x/y 不小于 mid,总共需要至少做 C 次操作
for(int i = 1; i <= n; i++) {
double v = (double)a[i] / (double)b[i];
int cur = (int)ceil(mid - v);
cur = max(0LL, cur);
C += cur;
if(C > m) return 0;
}
return 1;
};
int cnt = 100;
double l = 0, r = 2e14;
while(cnt--) {
double mid = (l + r) / 2;
if(check(mid)) l = mid;
else r = mid;
}
priority_queue<Node> q;
for(int i = 1; i <= n; i++) {
double v = (double)a[i] / (double)b[i];
int cur = (int)ceil(l - v);
cur = max(0LL, cur);
a[i] += cur * b[i];
m -= cur;
q.push({a[i], b[i], i});
}
while(m--) {
auto [x, y, id] = q.top();
q.pop();
a[id] += y;
q.push({a[id], y, id});
}
int ans = 1;
for(int i = 1; i <= n; i++) {
assert(a[i] <= 1e18);
a[i] %= MOD;
ans = (ans * a[i]) % MOD;
}
cout << ans << endl;
}
本次蓝桥拿到省一了,感觉还行,继续加油!!!

浙公网安备 33010602011771号