原创文章: https://www.huilon.net.cn/article/14
Codeforces 1353C
Description
题意
现有一个
n
×
n
n × n
n×n (n是奇数) 的空间, 共
n
2
n^2
n2 个格子, 在初始的时候, 每个各自有一个人. 人可以在相邻8个格子之间移动一个格子. 每次只能移动一个格子
示意图:
现在需要把
n
×
n
n × n
n×n 空间内所有人都召集在一个格子内, 求所有人走到一个格子内, 总共需要的最少移动的步骤数. 共
t
t
t 组数据输入
范围:
1 ≤ n ≤ 5 ⋅ 1 0 5 1≤ n ≤ 5 \cdot 10^5 1≤n≤5⋅105
n n n是指所有测试样例的n加起来的数
Tutorial
题解
我们拿样例 n = 5 n=5 n=5 来看看, 需要指定最少移动的步骤, 易证: 把所有人召集在最中间的格子, 所有人移动的步骤肯定最少.
现在不妨先把8个方向的人先召集过来, 如下图, 先把深灰色的两个人召集到 13 13 13, 一共走的步骤是 1 + 2 = 3 1+2=3 1+2=3, 其他同理. 值得注意的是, 黄色格子和灰色格子要走到 13 13 13 的步骤数是一样的, 他们可以斜着走. 总共走完之后的步骤数是 ( 1 + 2 ) × 8 (1+2) × 8 (1+2)×8
现在还剩下边边的 8 8 8 个人, 拿左上角做例子, 2 2 2 这个人可以往下走一步, 然后往 13 13 13 走一步, 共 2 2 2 步. 其他同理, 总共 2 × 8 2×8 2×8 步
我们再来看一般一点的情况, 比如 n = 7 n=7 n=7. 先把 8 8 8个方向的人都招过来, 如下图, 共 ( 1 + 2 + 3 ) × 8 (1+2+3)×8 (1+2+3)×8 步
然后再把里一圈的人招过来, 如下图, 这里一共 8 8 8 个人, 每个人只需走 2 2 2 步, 共 2 × 8 2×8 2×8 步
最后再把外一圈的人招过来, 如下图, 这里一共 16 16 16 个人, 每个人需要走 3 3 3 步, 共 3 × 16 3×16 3×16 步
归纳一下:
n=5 时, 步骤数为
( 1 + 2 ) × 8 + 2 × 8 (1+2)×8 + 2×8 (1+2)×8+2×8
n=7 时, 步骤数为
( 1 + 2 + 3 ) × 8 + 2 × 8 + 3 × 16 (1+2+3)×8 + 2×8 + 3×16 (1+2+3)×8+2×8+3×16
用观察法归纳可以写成
( 1 + 2 + 3 + . . . + ⌊ n 2 ⌋ ) × 8 + 1 × ( 8 × 0 ) + 2 × ( 8 × 1 ) + 3 × ( 8 × 2 ) + . . . + ( ⌊ n 2 ⌋ × ( 8 × ( ⌊ n 2 ⌋ − 1 ) ) ) \begin{aligned}&(1+2+3+ ... + \lfloor\frac{n}{2}\rfloor )×8\\+ &1×(8×0)\\+ &2×(8×1)\\+ &3×(8×2)\\+ &\ ...\\+ &(\lfloor\frac{n}{2}\rfloor×(8×(\lfloor\frac{n}{2}\rfloor-1)))\end{aligned} +++++(1+2+3+...+⌊2n⌋)×81×(8×0)2×(8×1)3×(8×2) ...(⌊2n⌋×(8×(⌊2n⌋−1)))
即
8 ∑ i = 1 ⌊ n 2 ⌋ i + ∑ i = 1 ⌊ n 2 ⌋ i × 8 ( i − 1 ) 8\sum\limits_{i=1}^{\lfloor\frac{n}{2}\rfloor}i + \sum\limits_{i=1}^{\lfloor\frac{n}{2}\rfloor}i×8(i-1) 8i=1∑⌊2n⌋i+i=1∑⌊2n⌋i×8(i−1)
用数学归纳法可证. 证明略.
向上取整, 运算称为 Ceiling,用数学符号 ⌈ n ⌉ ⌈n⌉ ⌈n⌉ (上有起止,开口向下)表示。
.
向下取整, 运算称为 Floor,用数学符号 ⌊ n ⌋ ⌊n⌋ ⌊n⌋ (下有起止,开口向上)表示。
.
注意,向上取整和向下取整是针对有浮点数而言的; 若整数向上取整和向下取整, 都是整数本身。
.
.
四舍五入:更接近自己的整数; 把小数点后面的数字四舍五入
即:如被舍去部分的头一位数字小于五,则舍去; 如大于等于五,则被保留部分的最后一位数字加1
向上取整:比自己大的最小整数;
向下取整:比自己小的最大整数;
.
取自网络
我们退一步, 就用
( 1 + 2 + 3 + . . . ) × 8 + 2 × 8 + 3 × 16 + . . . (1+2+3+...)×8 + 2×8 + 3×16 + ... (1+2+3+...)×8+2×8+3×16+...
这个公式来计算, 时间复杂度为 O ( m ) O(m) O(m)
有以下代码:
#include <iostream>
using namespace std;
int main() {
int t; cin >> t;
for (int _ = 0; _ < t; ++_) {
int n; cin >> n;
long long ans = 0; // 确保乘法和求和的计算结果正确, 使用 long long 类型.
// 1,2,3,...,m 求和公式: m*(m+1)/2
long long m = n/2; // n/2 向下取整
ans = (m*(m+1)/2)*8; // (1+2+3+...)*8
long long ans_tmp = 0;
for (int i = 2; i <= m; ++i) { // 当 n = 1 时, 不进去循环, 会输出 0
ans_tmp += 8; // + 2*8 + 3*16 + ...
ans += i * ans_tmp;
}
cout << ans << endl;
}
return 0;
}