C++11 编译命令

g++ A.cpp -o A -Wall -lm -std=c++11
./A

A. 移动距离

本题总分:5 分

问题描述

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

  1. 水平向右移动,即沿着 x 轴正方向移动一定的距离。
  2. 沿着一个圆心在原点 $(0, 0)$、以他当前位置到原点的距离为半径的圆的圆周移动,移动方向不限(即顺时针或逆时针移动不限)。在这种条件下,他到达目的地最少移动多少单位距离?你只需要输出答案四舍五入到整数的结果。

思路

最短路径应该是往右走圆的半径长度 $r = \sqrt{233^2 + 666^2}$,然后再延着圆弧走到 $(233, 666)$ 这个点。圆弧长度:$l = \alpha r = \arctan{\frac{666}{233}}r$ ($\alpha$ 是圆弧对应的圆心角,以弧度制表示)。答案是 $1576$。

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    double r = sqrt(233 * 233 + 666 * 666);
    double ang = atan(1.0 * 666 / 233);
    cout << (int)round(ang * r + r) << endl;
    return 0;
}

B. 客流量上限

本题总分:5 分

问题描述

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

  1. $A_1$, $A2$, $\ldots$, $A_{2025}$ 必须是 $1$ 至 $2025$ 的一个排列,即每个 $A_i$ 均是 $1$ 至 $2025$ 之间的整数,且所有 $A_i$ 互不相同。
  2. 对于任意分店 $i$ 和 $j$($1 ≤ i, j ≤ 2025$,$i$ 可等于 $j$),它们的客流量上限 $A_i$ 和 $A_j$ 的乘积不得超过 $i \times j + 2025$。这些约束旨在平衡各分店客流压力,确保服务质量和运营稳定性。现在,请你计算这样的分配方案究竟有多少种。由于答案可能很大,你只\需输出其对 $10^9 + 7$ 取余后的结果即可。

思路

  1. 当 $i = j$ 时可以得出,$a_i = \sqrt[]{i^2 + 2025}$
  2. 根据第 $1$ 个条件的必要性,可以写一个程序判断每一个位置有哪一些数可以满足这个条件,发现
    • 当 $i \ge 1013$ 时,$a_i \le i$。
    • 当 $i < 1013$ 时,$a_i \le i + 1$。
  3. 验证 $2$ 中结论的充分性,
    • 当 $i, j < 1013$ 时,$a_i \times a_j \le (i + 1) \times (j + 1) < i \times j + i + j + 1 < i \times j + 2025$ 满足题目条件。
    • 当 $i < 1013 \le j \le 2025$ 时,$a_i \times a_j \le (i + 1) \times j \le i \times j + j \le i \times j + 2025$ 满足题目条件。
    • 当 $i, j \ge 1013$ 时,$a_i \times a_j \le i \times j < i \times j + 2025$ 满足题目条件。
  4. 因此 $2$ 中结论是题目要求的充要条件。

代码

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int mod = 1e9 + 7;

int qpow(int x, int k) {
    int res = 1;
    while (k) {
        if (k & 1) res = res * x % mod;
        x = x * x % mod;
        k >>= 1;
    }
    return res;
}

signed main() {
    cout << qpow(2, 1012) << endl;
    return 0;
}

C. 可分解的正整数

时间限制: 1.0 s 内存限制:256.0 MB 本题总分:10 分

问题描述

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

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

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

现给定一组包含 $N$ 个正整数的数据 $A_1$, $A_2$, $\ldots$, $A_N$。如果某个 $A_i$ 能够表示为符合上述条件的连续整数序列中所有元素的和,则称 $A_i$ 是可分解的。

请你统计这组数据中可分解的正整数的数量。

输入格式

输入的第一行包含一个正整数 $N$,表示数据的个数。

第二行包含 $N$ 个正整数 $A_1$, $A_2$, $\ldots$, $A_N$,表示需要判断是否可分解的正整数序列。

输出格式

输出一个整数,表示给定数据中可分解的正整数的数量。

样例输入

3
3 6 15

样例输出

3

样例说明

所以可分解的正整数的数量为 $3$。

评测用例规模与约定

思路

$[1, 10^9]$ 的正整数里面,$1$ 无法被连续整数序列表示,剩余所有的数都可以被以 $1$ 或 $0$ $1$ 为中心的连续整数序列表示。例如,

代码

#include <bits/stdc++.h>
using namespace std;

int n, a, ans = 0;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a, ans += (a > 1);
    cout << ans << endl;
    return 0;
}

D. 产值调整

时间限制: 1.0 s 内存限制:256.0 MB 本题总分:10 分

问题描述

偏远的小镇上,三兄弟共同经营着一家小型矿业公司 “兄弟矿业”。公司旗下有三座矿山:金矿、银矿和铜矿,它们的初始产值分别用非负整数 $A$、$B$ 和 $C$ 表示。这些矿山的产出是小镇经济的核心,支撑着三兄弟和许多矿工家庭的生计。

然而,各矿山的产值波动剧烈,有时金矿收益高而银矿、铜矿低迷,有时则相反。这种不稳定性让公司收入难以预测,也常引发兄弟间的争执。为了稳定经营,三兄弟设计了一个公平的产值调整策略,每年执行一次,每次调整时,将根据当前的产值 $A$、$B$、$C$,计算新产值:

  1. 金矿新产值 $A$′ $= \left \lfloor\frac{B + C}{2} \right\rfloor$;
  2. 银矿新产值 $B$′ $= \left \lfloor\frac{A + C}{2} \right\rfloor$;
  3. 铜矿新产值 $C$′ $= \left \lfloor\frac{A + B}{2} \right\rfloor$。

其中,$\lfloor\rfloor$ 表示向下取整。例如,$\lfloor3.7\rfloor = 3$,$\lfloor5.2\rfloor = 5$。

计算出 $A$′、$B$′、$C$′ 后,同时更新:$A$ 变为 $A$′,$B$ 变为 $B$′,$C$ 变为 $C$′,作 为下一年调整的基础。

三兄弟认为这个方法能平衡产值波动,于是计划连续执行 $K$ 次调整。现在,请你帮他们计算,经过 $K$ 次调整后,金矿、银矿和铜矿的产值分别是多少。

输入格式

输入的第一行包含一个整数 $T$ ,表示测试用例的数量。

接下来的 $T$ 行,每行包含四个整数 $A$,$B$,$C$,$K$,分别表示金矿、银矿和铜矿的初始产值,以及需要执行的调整次数。

输出格式

对于每个测试用例,输出一行,包含三个整数,表示经过 $K$ 次调整后金矿、银矿和铜矿的产值,用空格分隔。

样例输入

2
10 20 30 1
5 5 5 3

样例输出

25 20 15
5 5 5

评测用例规模与约定

思路

三个数会迅速向某个数收敛,当三个数都一样时,无论进行多少次操作,结果都不会变了。在三个数都变成一样之前暴力求解。

代码

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int a, b, c, k;
    cin >> a >> b >> c >> k;
    while (k--) {
        int aa = (b + c) / 2, bb = (a + c) / 2, cc = (a + b) / 2;
        a = aa, b = bb, c = cc;
        if (a == b && b == c) break;
    }
    cout << a << " " << b << " " << c << "\n";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int T;
    cin >> T;
    while (T--) solve();
    return 0;
}

E. 画展布置

时间限制: 1.0 s 内存限制:256.0 MB 本题总分:15 分

问题描述

画展策展人小蓝和助理小桥为即将举办的画展准备了 $N$ 幅画作,其艺术价值分别为 $A_1$, $A_2$, $\ldots$, $A_N$。他们需要从这 $N$ 幅画中挑选 $M$ 幅,并按照一定顺序布置在展厅的 $M$ 个位置上。如果随意挑选和排列,艺术价值的变化可能会过于突兀,导致观众的观展体验不够流畅。

为了优化布置,他们查阅了《画展布置指南》。指南指出,理想的画展应使观众在欣赏画作时,艺术价值的过渡尽量平缓。指南建议,选择并排列 $M$ 幅画,应使艺术价值的变化程度通过一个数值 $L$ 来衡量,且该值越小越好。数值 $L$ 的定义为:

\[L = \sum_{i = 1}^{M - 1} | B_i^2 - B_{i + 1}^2 |\]

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

现在,他们希望通过精心挑选和排列这 $M$ 幅画作,使 $L$ 达到最小值,以提升画展的整体协调性。请你帮他们计算出这个最小值是多少。

输入格式

输入共两行。

第一行包含两个正整数 $N$ 和 $M$,分别表示画作的总数和需要挑选的画作数量。

第二行包含 $N$ 个正整数 $A_1$, $A_2$, $\ldots$, $A_N$,表示每幅画作的艺术价值。

输入样例

4 2
1 5 2 4

输出样例

3

样例用例规模与约定

输出格式

输出一个整数,表示 $L$ 的最小值。

思路

  1. 排序证明: 对于选定的若干个数,必须按照大小排序才能达到最优解。数学证明如下:
    • 对于任意 $i < j$,如果交换 $B_i$ 和 $B_j$ 的位置,即令 $B’_i = B_j$,$B’_j = B_i$,则必然满足不等式: \(|B_i^2 - B_{i-1}^2| + |B_i^2 - B_{i+1}^2| + |B_j^2 - B_{j-1}^2| + |B_j^2 - B_{j+1}^2| < |B'^2_i - B'^2_{i-1}| + |B'^2_i - B'^2_{i+1}| + |B'^2_j - B'^2_{j-1}| + |B'^2_j - B'^2_{j+1}|\) 这表明交换后结果会更差。
  2. 公式推导: 假设选定数字已按从小到大排序,则 $L$ 的计算公式应为: \(L = \sum_{i=1}^{M-1}|B_i^2 - B_{i+1}^2| = \sum_{i=1}^{M-1}(B_{i+1}^2 - B_i^2) = B_M^2 - B_1^2\) 因此需要使所选数的最大值与最小值的差尽可能小。

  3. 算法实现: 将所有数字排序,然后使用滑动窗口选取连续的 $M$ 个数,计算 $B_M^2 - B_1^2$ 的最小值。

代码

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 1e5 + 10;
int n, m, a[N], sum[N], ans = LLONG_MAX;

signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + n + 1);
    for (int i = 2; i <= n; i++)
        sum[i] = sum[i - 1] + a[i] * a[i] - a[i - 1] * a[i - 1];
    for (int l = 1; l + m - 1 <= n; l++) {
        int r = l + m - 1;
        ans = min(ans, sum[r] - sum[l]);
    }
    cout << ans << endl;
    return 0;
}

F. 水质检测

时间限制: 1.0 s 内存限制:256.0 MB 本题总分:15 分

问题描述

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

输入格式

输入共两行,表示一个 $2 \times n$ 的河床。

每行一个长度为 $n$ 的字符串,仅包含 #.,其中 # 表示已经存在的检测器,. 表示空白。

输出格式

输出共 $1$ 行,一个整数表示答案。

输入样例

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

样例输出

5

样例说明

其中一种方案:

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

增加了 5 个检测器。

评测用例规模与约定

思路

设 $dp_{0/1, i}$ 表示考虑从左往右到第 $0/1$ 行、第 $i$ 列且第 $0/1$ 行、第 $i$ 列为 # 最少需要铺设的检测器数量。

两种转移方法取最小值,具体状态转移方程看代码。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
int dp[2][N];
string s[2];

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> s[0] >> s[1];
    int n = s[0].size();
    s[0] = " " + s[0], s[1] = " " + s[1];
    int l = 1, r = 0;
    for (int i = 1; i <= n; i++) {
        if (s[0][i] == '#' || s[1][i] == '#') {
            l = i;
            break;
        }
    }
    for (int i = n; i; i--) {
        if (s[0][i] == '#' || s[1][i] == '#') {
            r = i;
            break;
        }
    }
    for (int i = l; i <= r; i++) {
        dp[0][i] = min(dp[0][i - 1], dp[1][i - 1] + (s[0][i - 1] == '.' && s[1][i] == '.')) + (s[0][i] == '.');
        dp[1][i] = min(dp[1][i - 1], dp[0][i - 1] + (s[1][i - 1] == '.' && s[0][i] == '.')) + (s[1][i] == '.');
    }
    cout << min(dp[0][r], dp[1][r]) << endl;
    return 0;
}

G. 生产车间

时间限制: 1.0 s 内存限制:512.0 MB 本题总分:20 分

题目描述

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

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

输入格式

输入共 $n + 1$ 行。

第一行为一个正整数 $n$。

第二行为 $n$ 个由空格分开的正整数 $w_1,w_2, \dots,w_n$。

后面 $n - 1$ 行,每行两个整数表示树上的一条边连接的两个结点。

输出格式

输出共一行,一个整数代表答案。

输入样例

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

样例输出

8

说明/提示

删掉结点 $4$、$9$ 后生产线满足条件,根结点 $1$ 每单位时间将打包出 $8$ 单位的成品。

评测用例规模与约定

H. 装修报价

时间限制: 1.0 s 内存限制:512.0 MB 本题总分:20 分

题目描述

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

然而,当老王提交数据后,他发现这套系统的运作方式并不透明:系统只会给出一个最终报价,而不会公开任何运算过程或中间步骤。

公司对此解释称,这套系统会依据某种内部算法,在每对相邻数字之间插入 $+$(加法)、$-$(减法)或 $\oplus$(异或)运算符,并按照特定优先级规则计算结果:异或运算优先级最高,其次是加减。但由于保密性,具体的运算符组合以及中间过程都不会对外公开。

为了验证系统报价是否合理,老王决定模拟其运作方式,尝试每种可能的运算符组合,计算出所有可能出现的结果的总和。如果最终报价明显超出这个范围,他就有理由怀疑系统存在异常或误差。只是老王年事已高,手动计算颇为吃力,便向你求助。

现在,请你帮老王算出所有可能的结果的总和。由于该总和可能很大,你只需提供其对 $10^9+7$ 取余后的结果即可。

输入格式

第一行输入一个整数 $N$,表示装修相关费用的项数。

第二行输入 $N$ 个非负整数 $A_1, A_2, \dots , A_N$,表示各项费用。

输出格式

输出一个整数,表示所有可能的总和对 $10^9 + 7$ 取余后的结果。

样例输入

3
0 2 5

样例输出

11

说明/提示

对于输入样例中的三个数 $A = [0, 2, 5]$,所有可能的运算符组合共有 $9$ 种。计算结果如下:

$0 \oplus 2 \oplus 5 = 7$ $0 \oplus 2 + 5 = 7$ $0 \oplus 2 - 5 = -3$ $0 + 2 \oplus 5 = 7$ $0 + 2 + 5 = 7$ $0 + 2 - 5 = -3$ $0 - 2 \oplus 5 = -7$ $0 - 2 + 5 = 3$ $0 - 2 - 5 = -7$

所有结果的总和为:

\[7 + 7 + (-3) + 7 + 7 + (-3) + (-7) + 3 + (-7) = 11\]

$11$ 对 $10^9 + 7$ 取余后的值依然为 $11$,因此,输出结果为 $11$。

评测用例规模与约定

思路

代码

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 1e5 + 10, mod = 1e9 + 7;
int n, a[N], pre[N], pw3[N];

signed main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);
    cin >> n;
    pw3[0] = 1;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        pw3[i] = pw3[i - 1] * 3 % mod;
        pre[i] = pre[i - 1] ^ a[i];
    }
    int ans = 0;
    for (int i = 1; i < n; i++) {
        ans = (ans + (pre[i] * 2 % mod) * pw3[n - i - 1] % mod) % mod;
    }
    ans = (ans + pre[n]) % mod;
    cout << ans << endl;
    return 0;
}

快读模板

inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
}

如果题目中有的变量需要用 long long 的话,可以直接在宏里面把 int 扩展到 long long,这样比较方便(如下图)。

#include <bits/stdc++.h>
using namespace std;
 
#define int long long
 
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
}
 
// 代码
 
signed main() {
    // 代码
    return 0;
}

对拍代码

整个对拍需要以下文件。bf.cpp 文件里是暴力代码,std.cpp 文件里是用了算法的代码,data.cpp 用来生成输入样例,pai.cpp 用来比较 bfout.txtstdout.txt 的结果是否相同。

注意:每次更改 bf.cppstd.cppdata.cpp 之后都需要重新编译之后再运行 pai.cpp 进行对拍。

接下来以输出 $a + b$ 的程序来说明。

img1

bf.cpp

#include <bits/stdc++.h>
using namespace std;
 
int main() {
    int a, b, oup = 0;
    cin >> a >> b;
    for (int i = 1; i <= a; i++) oup++;
    for (int i = 1; i <= b; i++) oup++;
    cout << oup;
    return 0;
}

std.cpp

#include <bits/stdc++.h>
using namespace std;
 
int main() {
    int a, b;
    cin >> a >> b;
    if (a > 0) cout << a << endl;
    cout << a + b << endl;
    return 0;
}

data.cpp

#include <bits/stdc++.h>
using namespace std;
 
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
 
int rand(int l, int r) {
	return uniform_int_distribution<int>(l, r)(rng);
}
 
int main() {
    srand(time(0));
    int a = rand(1, 100000000), b = rand(1, 100000000); // 随机生成两个数字
    cout << a << ' ' << b << endl; // 按照格式输出
    return 0;
}

这个 data.cp 如果直接用 rand() 函数随机性其实很差,所以要利用 mt19937 来生成随机数。

pai.cpp

可以不用自己创建 txt 文件,编译运行一次 pai.cpp 之后会自动生成相应 txt 文件。

#include <bits/stdc++.h>
using namespace std;
 
int main() {
    int t = 1;
    while (1) {
        printf("test%d: ", t++);
        system("data.exe > in.txt"); // 用 data.exe 生成输入样例,并存入 in.txt 文件中
        system("std.exe < in.txt > stdout.txt");
        // 将 in.txt 文件中的输入样例用来测试 std.cpp 中的代码,并将结果输出到 stdout.txt 文件中
        system("bf.exe < in.txt > bfout.txt");
        // 将 in.txt 文件中的输入样例用来测试 bf.cpp 中的代码,并将结果输出到 bf.out 文件中

        // 比较 stdout.txt 和 bfout.txt 文件是否一样,一样返回 false,不一样返回 true
        if (system("fc stdout.txt bfout.txt")) return 0;
    }
}

下图是 pai.cpp 运行后的输出结果,会显示输出不一样的地方。

img2

如果输出样例一样的话,会一直显示找不到差异。

img3

这个时候接着写下一道题就好了,让它在后台接着运行,有可能后面会出现不一样的地方。