2025年第十六届蓝桥杯大赛软件赛C/C++大学B组题解

第十六届蓝桥杯大赛软件赛C/C++大学B组题解

试题A: 移动距离

问题描述

小明初始在二维平面的原点,他想前往坐标(233,666)。在移动过程中,他只能采用以下两种移动方式,并且这两种移动方式可以交替、不限次数地使用:

  1. 水平向右移动,即沿着x轴正方向移动一定的距离。
  2. 沿着一个圆心在原点(0,0)、以他当前位置到原点的距离为半径的圆的圆周移动,移动方向不限(即顺时针或逆时针移动不限)。

在这种条件下,他到达目的地最少移动多少单位距离?你只需要输出答案四舍五入到整数的结果。

解题思路

这个问题可以转化为:如何从原点到达目标点,使得路径长度最小。

首先,我们可以观察到,如果目标点在x轴上,那么直接水平向右移动是最优的。

对于一般情况,我们可以分两步走:

  1. 先沿着x轴正方向移动到点(r,0),其中r是目标点到原点的距离
  2. 然后沿着半径为r的圆弧移动到目标点

目标点(233,666)到原点的距离为:
r = √(233² + 666²) = √(54289 + 443556) = √497845 ≈ 705.58

第一步移动距离为r = 705.58
第二步需要计算圆弧长度。目标点与x轴正方向的夹角为:
θ = arctan(666/233) ≈ 1.2323弧度

圆弧长度 = r·θ = 705.58 × 1.2323 ≈ 869.49

总移动距离 = 705.58 + 869.49 = 1575.07

四舍五入到整数为1575。

答案

1575

试题B: 客流量上限

问题描述

一家连锁旅馆在全国拥有2025个分店,分别编号为1至2025。随着节日临近,总部决定为每家分店设定每日客流量的上限,分别记作A₁,A₂,…,A₂₀₂₅。这些上限并非随意分配,而是需要满足以下约束条件:

  1. A₁,A₂,…,A₂₀₂₅必须是1至2025的一个排列,即每个A_i均是1至2025之间的整数,且所有A_i互不相同。
  2. 对于任意分店i和j(1≤i,j≤2025,i可等于j),它们的客流量上限A_i和A_j的乘积不得超过i×j+2025。

现在,请你计算这样的分配方案究竟有多少种。由于答案可能很大,你只需输出其对10^9+7取余后的结果即可。

解题思路

这个问题要求我们计算满足特定约束条件的排列数量。让我们逐步分析这个问题:

约束条件分析

  1. A₁, A₂, …, A₂₀₂₅ 必须是 1 到 2025 的一个排列
  2. 对于任意 i 和 j (1 ≤ i,j ≤ 2025),必须满足 Aᵢ × Aⱼ ≤ i × j + 2025

关键观察

首先,我们考虑当 i = j 时的特殊情况:

  • 此时约束条件变为:Aᵢ × Aᵢ ≤ i × i + 2025
  • 即:Aᵢ² ≤ i² + 2025
  • 因此:Aᵢ ≤ √(i² + 2025)

通过计算和分析,我们可以得出以下结论:

  1. 对于 1014 ≤ i ≤ 2025 的情况

    • 约束条件要求 Aᵢ ≤ i
    • 由于 Aᵢ 必须是 1 到 2025 之间的整数,且所有 Aᵢ 互不相同
    • 最优解是 Aᵢ = i(这样可以最大化其他位置的选择空间)
  2. 对于 1 ≤ i ≤ 1012 的情况

    • 约束条件允许 Aᵢ ≤ i + 1
    • 对于这些位置,我们有两种选择:Aᵢ = i 或 Aᵢ = i + 1
  3. 对于 1013 ≤ i ≤ 1013 的情况

    • 这个位置的值已经被前面的分析确定

组合计算

根据上述分析:

  • 对于位置 1014 到 2025(共 1012 个位置),每个位置的值唯一确定为 Aᵢ = i
  • 对于位置 1 到 1012(共 1012 个位置),每个位置有两种可能的选择
  • 因此,总的方案数为 2¹⁰¹²

由于答案可能很大,我们需要对 10⁹ + 7 取模。最终答案为:
2¹⁰¹² mod (10⁹ + 7) = 781448427

代码实现

#include <bits/stdc++.h>
using namespace std;const int MOD = 1e9 + 7;
const int N = 2025;// 计算2的幂次取模
long long pow_mod(long long base, long long exp, long long mod) {long long result = 1;base %= mod;while (exp > 0) {if (exp & 1) {result = (result * base) % mod;}base = (base * base) % mod;exp >>= 1;}return result;
}int main() {// 根据分析,答案为2^1012 mod (10^9 + 7)long long ans = pow_mod(2, 1012, MOD);cout << ans << endl;return 0;
}

答案:

781448427

试题C: 可分解的正整数

问题描述

定义一种特殊的整数序列,这种序列由连续递增的整数组成,并满足以下条件:

  1. 序列长度至少为3。
  2. 序列中的数字是连续递增的整数(即相邻元素之差为1),可以包括正整数、负整数或0。

例如,[1,2,3]、[4,5,6,7]和[-1,0,1]是符合条件的序列,而[1,2](长度不足)和[1,2,4](不连续)不符合要求。

现给定一组包含N个正整数的数据A₁,A₂,…,A_N。如果某个A_i能够表示为符合上述条件的连续整数序列中所有元素的和,则称A_i是可分解的。请你统计这组数据中可分解的正整数的数量。

解题思路

代码实现

#include<bits/stdc++.h>
using namespace std;int main(){int n;int t;int ans = 0;cin>>n;for(int i = 0;i < n;i++){cin>>t;if(t != 1){ans++;}}cout<<ans<<endl;return 0;
}

试题D: 产值调整

问题描述

偏远的小镇上,三兄弟共同经营着一家小型矿业公司"兄弟矿业"。公司旗下有三座矿山:金矿、银矿和铜矿,它们的初始产值分别用非负整数A、B和C表示。

为了稳定经营,三兄弟设计了一个产值调整策略,每年执行一次,每次调整时,将根据当前的产值A、B、C,计算新产值:

  1. 金矿新产值A′=⌊(B+C)/2⌋;
  2. 银矿新产值B′=⌊(A+C)/2⌋;
  3. 铜矿新产值C′=⌊(A+B)/2⌋。

其中,⌊⌋表示向下取整。计算出A′、B′、C′后,同时更新:A变为A′,B变为B′,C变为C′,作为下一年调整的基础。

三兄弟计划连续执行K次调整。现在,请你帮他们计算,经过K次调整后,金矿、银矿和铜矿的产值分别是多少。

解题思路

我们可以直接模拟这个过程,但由于K可能很大(最大10^9),直接模拟会超时。

观察调整规则,我们可以发现:

  1. 如果A=B=C,那么调整后仍然是A=B=C
  2. 如果不相等,每次调整后的值会趋向于平均值

实际上,经过足够多次调整后,三个值会变得相等或者在一个很小的范围内循环。

通过数学分析和实验,我们可以发现:

  1. 如果初始值全部相等,那么调整后仍然相等
  2. 如果不全相等,经过最多6次调整,三个值要么全部相等,要么会进入一个长度不超过3的循环

因此,我们可以先模拟前几次调整,然后根据情况决定最终结果。

代码实现

#include <bits/stdc++.h>
using namespace std;void adjust(long long& a, long long& b, long long& c) {long long new_a = (b + c) / 2;long long new_b = (a + c) / 2;long long new_c = (a + b) / 2;a = new_a;b = new_b;c = new_c;
}int main() {int T;cin >> T;while (T--) {long long A, B, C, K;cin >> A >> B >> C >> K;// 如果已经相等,不需要调整if (A == B && B == C) {cout << A << " " << B << " " << C << endl;continue;}while(K--){adjust(A,B,C);// 如果已经相等,不需要调整if (A == B && B == C) {break;}}cout << A << " " << B << " " << C << endl;}return 0;
}

答案

对于样例输入:

  1. A=10, B=20, C=30, K=1

  2. 一次调整后:A′=25, B′=20, C′=15

  3. A=5, B=5, C=5, K=3

  4. 初始值已经相等,调整后仍然是A=5, B=5, C=5

试题E: 画展布置

问题描述

画展策展人小蓝和助理小桥为即将举办的画展准备了N幅画作,其艺术价值分别为A₁,A₂,…,A_N。他们需要从这N幅画中挑选M幅,并按照一定顺序布置在展厅的M个位置上。

为了优化布置,他们希望使艺术价值的变化程度通过一个数值L来衡量,且该值越小越好。数值L的定义为:
L = Σ(i=1 to M-1) |B²ᵢ₊₁ - B²ᵢ|

其中B_i表示展厅第i个位置上画作的艺术价值。

现在,他们希望通过精心挑选和排列这M幅画作,使L达到最小值。请你帮他们计算出这个最小值是多少。

解题思路

这个问题要求我们从N幅画中选择M幅,并排列它们,使得相邻画作艺术价值平方的差的绝对值之和最小。

首先,我们可以观察到,对于任意两幅画的艺术价值a和b,|a² - b²| = |a-b|·|a+b|。这意味着,如果我们想要最小化|a² - b²|,我们应该选择艺术价值接近的画作放在相邻位置。

一个直观的策略是:

  1. 对所有画作的艺术价值进行排序
  2. 选择M幅连续的画作(因为连续的画作艺术价值差异最小)
  3. 按照特定顺序排列这M幅画作,使得L最小

对于排列顺序,我们可以证明,最优的排列方式是按照艺术价值从小到大或从大到小排列。

代码实现

#include <bits/stdc++.h>
using namespace std;int main() {int N, M;cin >> N >> M;vector<int> values(N);for (int i = 0; i < N; i++) {cin >> values[i];}// 排序sort(values.begin(), values.end());// 计算所有可能的连续M幅画作的L值long long min_L = LLONG_MAX;for (int i = 0; i <= N - M; i++) {vector<int> selected(values.begin() + i, values.begin() + i + M);// 计算按照从小到大排列的L值long long L1 = 0;for (int j = 0; j < M - 1; j++) {long long diff = (long long)selected[j+1] * selected[j+1] - (long long)selected[j] * selected[j];L1 += abs(diff);}// 计算按照从大到小排列的L值long long L2 = 0;for (int j = 0; j < M - 1; j++) {long long diff = (long long)selected[M-j-2] * selected[M-j-2] - (long long)selected[M-j-1] * selected[M-j-1];L2 += abs(diff);}min_L = min(min_L, min(L1, L2));}cout << min_L << endl;return 0;
}

答案

对于样例输入:
N=4, M=2, 艺术价值为[1, 5, 2, 4]

排序后为[1, 2, 4, 5]
可能的连续2幅画作为:[1,2], [2,4], [4,5]

计算L值:

  • [1,2]: |2² - 1²| = |4 - 1| = 3
  • [2,4]: |4² - 2²| = |16 - 4| = 12
  • [4,5]: |5² - 4²| = |25 - 16| = 9

最小的L值为3。

试题F: 水质检测

问题描述

小明需要在一条2×n的河床上铺设水质检测器。在他铺设之前,河床上已经存在一些检测器。如果两个检测器上下或者左右相邻,那么这两个检测器就是互相连通的。连通具有传递性,即如果A和B连通,B和C连通,那么A和C也连通。现在他需要在河床上增加铺设一些检测器使得所有的检测器都互相连通。他想知道最少需要增加铺设多少个检测器?

解题思路

这是一个连通性问题,可以使用并查集或深度优先搜索来解决。

首先,我们需要找出已有检测器形成的所有连通分量。然后,我们需要添加最少的检测器来连接这些分量。

对于每两个相邻的连通分量,我们只需要添加一个检测器就可以连接它们。因此,如果有k个连通分量,我们至少需要添加k-1个检测器。

但是,我们还需要考虑如何放置这些检测器,使得添加的数量最少。一种策略是:

  1. 对于每个连通分量,找出其边界点
  2. 对于相邻的两个连通分量,找出它们之间的最短路径
  3. 在这条路径上添加检测器

代码实现

#include <iostream>
#include <vector>
#include <queue>
#include <string>
using namespace std;const int dx[4] = {0, 1, 0, -1};
const int dy[4] = {1, 0, -1, 0};int main() {string row1, row2;cin >> row1 >> row2;int n = row1.length();vector<vector<char>> grid(2, vector<char>(n));for (int j = 0; j < n; j++) {grid[0][j] = row1[j];grid[1][j] = row2[j];}// 标记连通分量vector<vector<int>> component(2, vector<int>(n, -1));int comp_id = 0;for (int i = 0; i < 2; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == '#' && component[i][j] == -1) {// BFS标记连通分量queue<pair<int, int>> q;q.push({i, j});component[i][j] = comp_id;while (!q.empty()) {auto [x, y] = q.front();q.pop();for (int d = 0; d < 4; d++) {int nx = x + dx[d];int ny = y + dy[d];if (nx >= 0 && nx < 2 && ny >= 0 && ny < n && grid[nx][ny] == '#' && component[nx][ny] == -1) {component[nx][ny] = comp_id;q.push({nx, ny});}}}comp_id++;}}}// 如果没有检测器或只有一个连通分量,特殊处理if (comp_id == 0) {cout << 0 << endl;return 0;}if (comp_id == 1) {cout << 0 << endl;return 0;}// 计算连通分量之间的最短距离vector<vector<int>> dist(comp_id, vector<int>(comp_id, INT_MAX));for (int c1 = 0; c1 < comp_id; c1++) {// BFS计算从c1到其他连通分量的最短距离vector<vector<int>> distance(2, vector<int>(n, -1));queue<pair<int, int>> q;for (int i = 0; i < 2; i++) {for (int j = 0; j < n; j++) {if (component[i][j] == c1) {q.push({i, j});distance[i][j] = 0;}}}while (!q.empty()) {auto [x, y] = q.front();q.pop();for (int d = 0; d < 4; d++) {int nx = x + dx[d];int ny = y + dy[d];if (nx >= 0 && nx < 2 && ny >= 0 && ny < n && distance[nx][ny] == -1) {distance[nx][ny] = distance[x][y] + 1;q.push({nx, ny});if (grid[nx][ny] == '#') {int c2 = component[nx][ny];if (c2 != c1) {dist[c1][c2] = min(dist[c1][c2], distance[nx][ny] - 1);}}}}}}// 使用最小生成树算法连接所有连通分量vector<bool> visited(comp_id, false);visited[0] = true;int total_dist = 0;for (int i = 1; i < comp_id; i++) {int min_dist = INT_MAX;int next_comp = -1;for (int j = 0; j < comp_id; j++) {if (visited[j]) {for (int k = 0; k < comp_id; k++) {if (!visited[k] && dist[j][k] < min_dist) {min_dist = dist[j][k];next_comp = k;}}}}visited[next_comp] = true;total_dist += min_dist;}cout << total_dist << endl;return 0;
}

答案

对于样例输入:

.##.....#
.#.#.#...

已有的检测器形成了4个连通分量。要使它们全部连通,需要添加5个检测器。

试题G: 生产车间

问题描述

小明正在改造一个生产车间的生产流水线。这个车间共有n台设备,构成以1为根结点的一棵树,结点i有权值w_i。其中叶节点的权值w_i表示每单位时间将产出w_i单位的材料并送往父结点,根结点的权值w_i表示每单位时间内能打包多少单位成品,其他结点的权值w_i表示每单位时间最多能加工w_i单位的材料并送往父结点。

由于当前生产线中某些结点存在产能不够的问题导致生产线无法正常运行,即存在某些结点每单位时间收到的材料超过了当前结点的加工能力上限。小明计划删除一些结点使得所有结点都能正常运行。他想知道删除一些结点后根结点每单位时间内最多能打包多少单位的成品?

解题思路

这个问题描述了一个生产流水线优化场景:

  • 有n台设备构成一棵以1为根的树
  • 每个节点有权值w_i,表示其加工能力
  • 叶节点产生材料,非叶节点加工材料,根节点打包成品
  • 如果节点接收的材料超过其加工能力,生产线无法正常运行
  • 可以删除一些节点(及其子树)使所有节点正常运行
  • 目标是最大化根节点的产出

关键洞察:这是一个树形结构上的优化问题,我们需要决定保留或删除每个节点,以使根节点的产出最大化。要注意并不是每个节点的最大值组合起来并就一定是所求的最优解,比如样例数据,样例解释见代码后

我们采用自底向上的方法,从叶子节点开始,计算每个节点在不同情况下可能的产出值:

  1. 对于每个节点,我们有两个基本选择:

    • 保留节点:节点产生其权值的产出(对于叶子节点)或处理子节点的材料(对于非叶子节点)
    • 删除节点:节点产出为0
  2. 对于非叶子节点,我们需要:

    • 收集所有子节点的可能产出值
    • 组合这些产出值,确保总和不超过当前节点的加工能力
    • 计算当前节点所有可能的产出值
  3. 最终,根节点的最大可能产出值就是答案

代码实现

#include <bits/stdc++.h>
using namespace std;const int MAX_NODES = 1005;
vector<int> g[MAX_NODES]; // 树结构的邻接表
int n, capacity[MAX_NODES]; // 节点数和各节点容量/*** 计算以current_node为根的子树能提供的合法材料数值集合* @param current_node 当前处理的节点* @param parent_node 父节点(防止回溯)* @return 包含所有可能值的有序集合*/
set<int> dfs(int current_node, int parent_node) {// g[current_node].size() == 1 表示无向图的叶子节点//parent_node != 0 避免出现链状if (g[current_node].size() == 1 && parent_node != 0) {return {capacity[current_node], 0}; // 保留或关闭该节点}set<int> valid_sums = {0}; // 初始化,包含0(全关闭情况)// 使用范围for循环遍历邻接节点for (int child_node : g[current_node]) {if (child_node == parent_node) continue; // 跳过父节点auto child_outputs = dfs(child_node, current_node);//深入遍历子节点set<int> current_sums = valid_sums; // 当前状态的副本//依次将任意两个子节点的所有取值进行组合for (int parent_sum : current_sums) {for (int child_contribution : child_outputs) {int total = parent_sum + child_contribution;if (total <= capacity[current_node]) {valid_sums.insert(total);}}}}return valid_sums;//返回该节点的所有取值情况
}int main() {// 输入处理cin >> n;for (int i = 1; i <= n; ++i) {cin >> capacity[i];}// 构建树结构for (int i = 1; i < n; ++i) {int node_a, node_b;cin >> node_a >> node_b;g[node_a].push_back(node_b);g[node_b].push_back(node_a);}// 计算并输出最大打包量cout << *dfs(1, 0).rbegin() << endl;return 0;
}

注意:这份代码在部分网站测试会有极少量测试集超时,对此可以使用使用位运算bitset代替集合

#include <bits/stdc++.h>
using namespace std;const int MAXN = 1005;
const int MAXW = 1001;  // 最大权值+1int n;                          // 节点数量
int w[MAXN];                    // 每个节点的权值(容量)
vector<int> graph[MAXN];        // 无向图邻接表/*** 计算以u为根的子树能提供的合法材料数值集合* 使用bitset优化状态表示和组合操作* @param u 当前处理的节点* @param parent 父节点(防止回溯)* @return 包含所有可能值的位向量*/
bitset<MAXW> dfs(int u, int parent) {// 判断是否为叶子节点if (graph[u].size() == 1 && parent != 0) {bitset<MAXW> result;result[0] = 1;          // 可以产出0(删除节点)result[w[u]] = 1;       // 可以产出w[u](保留节点)return result;}// 初始化结果位向量,只有0位为1(表示所有子节点都关闭的情况)bitset<MAXW> result;result[0] = 1;// 遍历所有子节点for (int v : graph[u]) {if (v == parent) continue;  // 跳过父节点// 递归计算子节点的所有可能产出bitset<MAXW> child_outputs = dfs(v, u);// 保存当前结果的副本bitset<MAXW> current = result;result.reset();  // 清空结果// 优化的组合操作for (int i = 0; i <= w[u]; i++) {if (current[i]) {// 对于当前值i,与子节点的每个可能产出组合for (int j = 0; j <= w[u] - i; j++) {if (child_outputs[j]) {result[i + j] = 1;}}}}}return result;  // 返回所有可能的产出值
}int main() {ios_base::sync_with_stdio(false);  // 优化输入输出cin.tie(nullptr);cin >> n;// 读取每个节点的权值for (int i = 1; i <= n; i++) {cin >> w[i];}// 读取树的边for (int i = 0; i < n - 1; i++) {int u, v;cin >> u >> v;graph[u].push_back(v);graph[v].push_back(u);}// 计算根节点的所有可能产出bitset<MAXW> root_outputs = dfs(1, 0);// 找出最大可能产出int ans = 0;for (int i = MAXW - 1; i >= 0; i--) {if (root_outputs[i]) {ans = i;break;}}cout << ans << endl;return 0;
}

样例详细阐述

让我们以给定的样例来详细说明算法的执行过程:

9
9 7 3 7 1 6 2 2 7
1 2
1 3
2 4
2 5
2 6
6 7
6 8
6 9

树的结构如下:

    1/ \2   3/|\
4 5 6/|\7 8 9

节点权值:w = [9, 7, 3, 7, 1, 6, 2, 2, 7](索引从1开始)

执行DFS过程:

  1. 叶子节点

    • 节点3:返回3, 0
    • 节点4:返回7, 0
    • 节点5:返回1, 0
    • 节点7:返回2, 0
    • 节点8:返回2, 0
    • 节点9:返回7, 0
  2. 节点6(容量为6):

    • 初始结果集:0

    • 处理子节点7(2, 0):结果集变为0, 2

    • 处理子节点8(2, 0):结果集变为0, 2, 4

    • 处理子节点9(7, 0):

    • 0 + 0 = 0,已在结果集中

    • 0 + 7 = 7 > 6,超过容量,不添加

    • 2 + 0 = 2,已在结果集中

    • 2 + 7 = 9 > 6,超过容量,不添加

    • 4 + 0 = 4,添加到结果集

    • 4 + 7 = 11 > 6,超过容量,不添加

    最终结果集:0, 2, 4

  3. 节点2(容量为7):

    • 初始结果集:0

    • 处理子节点4(7, 0):结果集变为0, 7

    • 处理子节点5(1, 0):结果集变为0, 1, 7, 8,但8 > 7,所以实际为0, 1, 7

    • 处理子节点6(0, 2, 4):

    • 组合后得到0, 1, 2, 4, 7, 3 (2 + 1), 9 (2 + 7 应舍去), 5(4 + 1), 11(4 + 7 应舍去)

最终结果集:0, 1, 2, 3, 4, 5, 7

  1. 根节点1(容量为9):
    • 初始结果集:0

    • 处理子节点2(0, 1, 2, 3, 4, 5, 7):结果集变为0, 1, 2, 3, 4, 5, 7

    • 处理子节点3(3, 0):

    • 组合后得到0, 1, 2, 3, 4, 5, 6 (3 + 3), 7 (4 + 3), 8 (5 + 3),10 (7 + 1),但10 > 9,所以实际为0, 1, 2, 3, 4, 5, 6, 7, 8

最终结果集:0, 1, 2, 3, 4, 5, 6, 7, 8

  1. 最终答案:根节点1的结果集中的最大值为8

试题H: 装修报价

问题描述

老王计划装修房子,联系了一家装修公司。该公司有一套自动报价系统,只需用户提供N项装修相关费用A₁,A₂,…,A_N,系统便会根据这些费用生成最终的报价。

系统会依据某种内部算法,在每对相邻数字之间插入+(加法)、−(减法)或⊕(异或)运算符,并按照特定优先级规则计算结果:异或运算优先级最高,其次是加减。

老王决定模拟其运作方式,尝试每种可能的运算符组合,计算出所有可能出现的结果的总和。请你帮老王算出所有可能的结果的总和。由于该总和可能很大,你只需提供其对10^9+7取余后的结果即可。

方法一:前缀异或枚举法

解题思路

核心思想是枚举前缀异或的长度,计算每个前缀异或在所有可能组合中的贡献,然后求和。

关键洞察

  1. 考虑给表达式的第一个值前面也补上+号,例如:2−3⊕4 实际上是 +2−3⊕4
  2. 由于异或运算的优先级高于加减法,连续的异或运算结果可以合并成一个数字,使表达式变成只包含+和-的形式
  3. 所有的"+XXX"和"-XXX"在不同的式子中会同时出现,它们的贡献会相互抵消,也就是:对组合的式子分割为A+B,那么一定存在A-B的组合情况,这两者相加就会使得B相互抵消了,仅有A有贡献值
  4. 最终结果有贡献的只有前缀异或

对于长度为i的前缀异或S[i](即A[1] ⊕ A[2] ⊕ … ⊕ A[i]),其贡献计算方式为:

  • 第i+1个位置的运算符必须是+或-(2种选择,即把前面i个运算符的结果看作是一个数字A,后面的运算看作一个数字B),不能是异或(A + B / A - B)
  • 其中,第i+2到第n个位置的运算符可以是任意三种(3^(n-i-1)种选择,无论是哪一种都有对应的组合将其抵消掉)
  • 因此,贡献为:S[i] * 2 * 3^(n-i-1)

**特殊情况:**当i=n时(即所有数字都异或),贡献为S[i]。

最后的总结果就是所有前缀异或的加权和:

结果 = S_1 * 2 * 3^(n-2) + S_2 * 2 * 3^(n-3) + ... + S_(n-1) * 2 * 3^0 + S_n * 1

代码实现

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
const int MOD = 1e9 + 7;
vector<int> a;// 优化计算a^b % MOD
ll cal(ll a, ll b) {ll res = 1;while (b > 0) {//b是奇数,先乘掉一个aif (b % 2 == 1) res = (res * a) % MOD;//a^10 = (a ^ 2) ^ 5从而降低循环次数a = (a * a) % MOD;//指数b /= 2b /= 2;}return res;
}int main() {int n;cin >> n;// 只有一个数字if (n == 1) {int x;cin >> x;cout << x << endl;//直接输出return 0;}a.resize(n + 1);//存储n个数字for (int i = 1; i <= n; i++) {cin >> a[i];}ll ans = 0;ll s = 0;//存储异或值(即a[1] ⊕ a[2] ⊕ ... ⊕ a[i])for (int i = 1; i <= n; i++) {s ^= a[i];if (i < n) {// 不是最后一个数字:贡献为 s * 2 * 3^(n-i-1)ll cnt = (s * 2) % MOD;cnt = (cnt * cal(3, n - i - 1)) % MOD;ans = (ans + cnt) % MOD;} else {// 是最后一个数字:贡献为 sans = (ans + s) % MOD;}}cout << ans << endl;return 0;
}

样例分析

以样例 [0, 2, 5] 为例,详细演示算法执行过程:

初始状态

  • n = 3
  • A = [0, 0, 2, 5](代码执行时索引从1开始)
  • ans = 0
  • s= 0

第1次迭代 (i=1, A[1]=0)

  • s= 0 ^ 0 = 0
  • i < n,计算贡献:
  • cnt = 0 * 2 * 3^(3-1-1) = 0 * 2 * 3^1 = 0 * 2 * 3 = 0
  • ans = 0 + 0 = 0

第2次迭代 (i=2, A[2]=2)

  • s = 0 ^ 2 = 2

  • i < n,计算贡献:

  • cnt = 2 * 2 * 3^(3-2-1) = 2 * 2 * 3^0 = 2 * 2 * 1 = 4

  • ans = 0 + 4 = 4

第3次迭代 (i=3, A[3]=5)

  • s = 2 ^ 5 = 7

  • i = n,计算贡献:

  • cnt = 7

  • ans = 4 + 7 = 11

最终答案为11,与样例一致。

方法二:递推关系法

解题思路

核心思想是根据方法一原理,利用递推关系直接计算所有可能结果的总和,避免枚举所有3^(N-1)种组合。

关键洞察

  1. 对于除第一项外的每一项,其贡献总和为0。这是因为对于任意非第一项的数字A[i],如果我们将其前面的+改为-(或将-改为+),而保持其他运算符不变,这两种情况的结果会互为相反数,在总和中相互抵消。

    • 例如数据a,b,c : a + b + c; a + b - c; a + b ⊕ c 我们可以用a - b + c; a - b - c; a - b ⊕ c 完全消去b的贡献
  2. 唯一有贡献的是从第一项开始的连续异或序列(前缀异或)。

基于这一洞察,我们可以推导出递推关系:

  • 设S[k]表示前k个数的异或和:S[k] = A[1] ⊕ A[2] ⊕ … ⊕ A[k]
  • 设Ans[k]表示考虑前k个数字时所有可能结果的总和

递推关系为:

Ans[k] = 3 * Ans[k-1] - S[k-1] + S[k]

这个递推关系可以这样理解:

  • 当考虑第k个数字时,该数字前的符号有3种可能(添加+、-或⊕),所以乘以3
  • 需要减去前k-1个数字的异或和S[k-1]的贡献,因为在计算3*Ans[k-1]时重复计算了
  • 需要加上包含第k个数字的新异或和,S[k]的贡献

代码解释

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
const int MOD = 1e9 + 7;int main() {int n;cin >> n;ll ans = 0;  // 总答案ll s = 0;  // 当前前缀异或和for (int i = 1; i <= n; i++) {int x;cin >> x;// 更新答案:ans = ans * 3 - s + (s ^ x)ans = (ans * 3 - s + (s ^ x) + MOD) % MOD;// 更新前缀异或和s ^= x;}cout << ans << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/82901.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BGP实验练习2

需求&#xff1a; 1.AS1存在两个环回&#xff0c;一个地址为192.168.1.0/24&#xff0c;该地址不能再任何协议中宣告 AS3存在两个环回&#xff0c;该地址不能再任何协议中宣告 AS1还有一个环回地址为10.1.1.0/24&#xff0c;AS3另一个环回地址是11.1.1.0/24 最终要求这两…

【温湿度物联网】记录1:寄存器配置

一&#xff0c;及哦地址 基地址base的定义&#xff1a; ↓ 定义完是这个&#xff1a; GPIOA的地址就是以上的代表 2寄存器&#xff1a; 通过bsrr来改变odr寄存器&#xff0c;左移16位就是把0-15位的给移到高位的保留区&#xff0c;这样就归零了 3&#xff0c;项目寄存器实操…

MCP项目实例 - client sever交互

1. 项目概述 项目目标 构建一个本地智能舆论分析系统。 利用自然语言处理和多工具协作&#xff0c;实现用户查询意图的自动理解。 进行新闻检索、情绪分析、结构化输出和邮件推送。 系统流程 用户查询&#xff1a;用户输入查询请求。 提取关键词&#xff1a;从用户查询中…

运维体系架构规划

运维体系架构规划是一个系统性工程&#xff0c;旨在构建高效、稳定、安全的运维体系&#xff0c;保障业务系统的持续运行。下面从规划目标、核心模块、实施步骤等方面进行详细阐述&#xff1a; 一、规划目标 高可用性&#xff1a;确保业务系统 724 小时不间断运行&#xff0c…

zst-2001 上午题-历年真题 计算机网络(16个内容)

网络设备 计算机网络 - 第1题 ac 计算机网络 - 第2题 d 计算机网络 - 第3题 集线器不能隔离广播域和冲突域&#xff0c;所以集线器就1个广播域和冲突域 交换机就是那么的炫&#xff0c;可以隔离冲突域&#xff0c;有4给冲突域&#xff0c;但不能隔离广播域&#xf…

Python之with语句

文章目录 Python中的with语句详解一、基本语法二、工作原理三、文件操作中的with语句1. 基本用法2. 同时打开多个文件 四、with语句的优势五、自定义上下文管理器1. 基于类的实现2. 使用contextlib模块 六、常见应用场景七、注意事项 Python中的with语句详解 with语句是Python…

我的五周年创作纪念日

五年前的今天&#xff0c;我在CSDN发布了第一篇《基于VS2015的MFC学习笔记&#xff08;常用按钮button&#xff09;》&#xff0c;文末那句"欢迎交流"的忐忑留言&#xff0c;开启了这段充满惊喜的技术旅程。恍然发觉那些敲过的代码早已成长为参天大树。 收获 获得了…

Realtek 8126驱动分析第四篇——multi queue相关

Realtek 8126是 5G 网卡&#xff0c;因为和 8125 较为接近&#xff0c;第四篇从这里开始也无不可。本篇主要是讲 multi queue 相关&#xff0c;其他的一些内容在之前就已经提过&#xff0c;不加赘述。 1 初始化 1.1 rtl8126_init_one 从第一篇我们可以知道每个 PCI 驱动都注…

使用PHP对接日本股票市场数据

本文将介绍如何通过StockTV提供的API接口&#xff0c;使用PHP语言来获取并处理日本股票市场的数据。我们将以查询公司信息、查看涨跌排行榜和实时接收数据为例&#xff0c;展示具体的操作流程。 准备工作 首先&#xff0c;请确保您已经从StockTV获得了API密钥&#xff0c;并且…

爬虫工具与编程语言选择指南

有人问爬虫如何选择工具和编程语言。根据我多年的经验来说&#xff0c;是我肯定得先分析不同场景下适合的工具和语言。 如果大家不知道其他语言&#xff0c;比如JavaScript&#xff08;Node.js&#xff09;或者Go&#xff0c;这些在特定情况下可能更合适。比如&#xff0c;如果…

C语言while循环的用法(非常详细,附带实例)

while 是 C 语言中的一种循环控制结构&#xff0c;用于在特定条件为真时重复执行一段代码。 while 循环的语法如下&#xff1a; while (条件表达式) { // 循环体&#xff1a;条件为真时执行的代码 } 条件表达式&#xff1a;返回真&#xff08;非 0&#xff09;或假&#x…

1.短信登录

1.0 问题记录 1.0.1 redis 重复 token 问题 每次用户登录时&#xff0c;后端会创建一个新的 token 并存入 Redis&#xff0c;但之前登录的 token 还没有过期。这可能会导致以下问题&#xff1a; 1. Redis 中存在大量未过期但实际已不使用的 token2. 同一用户可能有多个有效 …

需求与技术实现不匹配,如何协调

协调需求与技术实现不匹配问题&#xff0c;需要加强技术参与需求阶段、推动架构与需求同步设计、建立跨职能沟通机制&#xff0c;其中加强技术参与需求阶段是最关键的一步。 需求如果脱离技术实际&#xff0c;就容易导致实现困难、资源浪费甚至项目失败。根据麦肯锡的一项研究&…

java每日精进 5.11【WebSocket】

1.纯Websocket实现消息发送 1.1一对一发送 前端 用户在输入框输入消息内容(sendText) 选择特定接收用户(sendUserId) 点击发送按钮触发handlerSend方法 构造消息内容JSON: {text: "Hello", // 消息内容toUserId: 123 // 目标用户ID } 包装为WebSocket标准格式…

【NextPilot日志移植】params.c解析

params.c 参数说明 params.c 文件的主要作用是定义与 SD卡日志记录 相关的参数。这些参数用于配置日志记录的行为&#xff0c;包括日志记录的时间、内容、存储管理以及加密设置等。 1. UTC 偏移量 (SDLOG_UTC_OFFSET) PARAM_DEFINE_INT32(SDLOG_UTC_OFFSET, 0);用途&#xf…

jFinal 使用 SolonMCP 开发 MCP(拥抱新潮流)

MCP 官方的 java-sdk 目前只支持 java17。直接基于 mcp-java-sdk 也比较复杂。使用 SolonMCP&#xff0c;可以基于 java8 开发&#xff08;像 MVC 的开发风格&#xff09;&#xff0c;且比较简单。 1、SolonMCP 简介 SolonMCP&#xff08;全称&#xff1a;solon-ai-mcp&#…

“端 - 边 - 云”三级智能协同平台的理论建构与技术实现

摘要 随着低空经济与智能制造的深度融合&#xff0c;传统集中式云计算架构在实时性、隐私保护和资源效率上的瓶颈日益凸显。本文提出“端 - 边 - 云”三级智能协同平台架构&#xff0c;以“时空 - 资源 - 服务”三维协同理论为核心&#xff0c;构建覆盖终端感知、边缘计算、云端…

【如何搭建开发环境】

了解java程序 JAVA体系结构 跨平台原理与编译和反编译 如何学习java语言&#xff0c;如何搭建环境 设置JAVA_HOME&#xff0c;指向jdk的安装目录这一级即可。比如我的JDK安装在C:\java\jdk1.8.0_25&#xff0c;那JAVA_HOME的值就是C:\java\jdk1.8.0_25设置Path变量 在Path值后…

LegoGPT,卡内基梅隆大学推出的乐高积木设计模型

LegoGPT 是由卡内基梅隆大学开发的一款创新性乐高积木设计模型&#xff0c;能够根据用户的文本提示生成结构稳固、可组装的乐高模型。该模型基于自回归语言模型和大规模乐高设计数据集进行训练&#xff0c;用户只需输入简单的文字描述&#xff0c;LegoGPT 就能逐步构建出物理稳…

深入理解 NumPy:Python 科学计算的基石

在数据科学、人工智能和科学计算的世界里&#xff0c;NumPy 是一块绕不过去的基石。它是 Python 语言中用于高性能科学计算的基础包&#xff0c;几乎所有的数据分析与机器学习框架&#xff08;如 Pandas、TensorFlow、Scikit-learn&#xff09;都离不开它的支持。 一、什么是 …