2026第十七届蓝桥杯c++b组省赛

2026第十七届蓝桥杯c++b组省赛

蓝桥杯省赛自测(CB 组) - 洛谷

A-青春常数

[P16232 蓝桥杯 2026 省 B] 青春常数 - 洛谷

1777275250546

第一题很明显的就是除以二再加一,因为0<=x然后x<y所以当时直接用计算器算的直接就是得到答案。

#include<bits/stdc++.h> 
using namespace std;
signed main(){
    cout<<"1013101260121012";
	return 0;
}

B-双碳战略

[P16233 蓝桥杯 2026 省 B] 双碳战略 - 洛谷

1777275454497

题目大意

有 2026 盏排成一条直线的智能路灯,初始全部亮着

总共有 22026 种状态,每种状态都能被操作到达。

对于执行的操作:

  1. 第奇数次操作:选一盏灯 i,翻转灯 i 和它右边所有灯。
  2. 第偶数次操作:选一盏灯 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] 循环右移 - 洛谷

1777286076966

循环右移的本质就是把最后一个数字移到第一个数字的位置,对某个数组,想要不管怎么进行循环右移都与原来相同,那么就要要求每个数字都相同,也就是说方案数就是范围内数字的个数,注意判断范围错误的情况。

输入 #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] 蓝桥竞技 - 洛谷

1777286789591

输入 #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**ik

这两个条件加起来就是充要条件。

#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 聚合 - 洛谷

1777287667460

输入 #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] 应急布线 - 洛谷

1777289336400

输入 #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] 理想温度 - 洛谷

1777289911895

输入 #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,在这个区间找出现最多的数,当然一开始还有要预处理一下这个的,然后就可以;

1777290265662

#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;
} 

1777291015476

H-足球训练

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

1777291282936

输入 #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 是最大的,而应该让增幅最大。

让我们用下面这个更一般化的例子看一看:

现在有 npair,现在我们要决策当前这次操作给 i=1 还是 i=2。也就是说,原本的乘积是:某定值 S×xx2(其中 S 表示其余 n−2 个 x 的乘积),现在我们让 x1 变成 x1+y1,贡献变成了:

(x1+y1)×xS

如果我们给 x2 操作,那么贡献会反过来变成:

(x2+y2)×xS

假设最终给 x1 操作更好,也就是说:(x1+y1)×xS>(x2+y2)×xS 的话,我们推导一下。

两边的 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) 的时间内把它应做的所有操作都做完。(简单推个式子即可)

于是我们以任意顺序处理完所有 npair 即可,但这里又出现了新的问题,实际上有可能在所有 pair 都操作到不小于 mi**d 后,m 还可能有剩下的,这个问题出现的原因就好比:{2,2,2} 和 {2,3,3} 这两个数组的最小值都是 2 一样。

但这时,我们一定可以证明的是,此时的 m 在给所有 pair 都操作到其 y**xm 后,m 一定严格小于 n

这是因为,如果 mn,那就意味着:我们至少可以把 npair 每人都再操作一次,于是当前二分出来的 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;
}

本次蓝桥拿到省一了,感觉还行,继续加油!!!

posted @ 2026-05-27 15:01  Godjian  阅读(68)  评论(0)    收藏  举报