打发时间。
本题总分: 5 5 5 分
问题描述
小明要做一个跑步训练。
初始时,小明充满体力,体力值计为 10000 10000 10000。如果小明跑步,每分钟损耗 600 600 600 的体力。如果小明休息,每分钟增加 300 300 300 的体力。体力的损耗和增加都是均匀变化的。
小明打算跑一分钟、休息一分钟、再跑一分钟、再休息一分钟 …… 如此循环。如果某个时刻小明的体力到达 0 0 0,他就停止锻炼。
请问小明在多久后停止锻炼。为了使答案为整数,请以秒为单位输出答案。答案中只填写数,不填写单位。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
3880
#include
int N = 10000, k = 5, ans = 0, K[2]{ 1, -1};
int main() {
for (int opt = 1; N > 0; opt ^= 1)
for (int i = 0; i < 60 && N > 0; i++)
N += K[opt] * (opt + 1) * k, ++ans;
if (N < 0) --ans;
printf("%d", ans);
}
简单的模拟题。
本题总分: 5 5 5 分
问题描述
新冠疫情由新冠病毒引起,最近在 A \mathrm A A 国蔓延,为了尽快控制疫情, A \mathrm A A 国准备给大量民众进病毒核酸检测。
然而,用于检测的试剂盒紧缺。
为了解决这一困难,科学家想了一个办法:合并检测。即将从多个人( k k k 个)采集的标本放到同一个试剂盒中进行检测。如果结果为阴性,则说明这 k k k 个人都是阴性,用一个试剂盒完成了 k k k 个人的检测。如果结果为阳性,则说明至少有一个人为阳性,需要将这 k k k 个人的样本全部重新独立检测(从理论上看,如果检测前 k − 1 k − 1 k−1 个人都是阴性可以推断出第 k k k 个人是阳性,但是在实际操作中不会利用此推断,而是将 k k k 个人独立检测),加上最开始的合并检测,一共使用了 k + 1 k + 1 k+1 个试剂盒完成了 k k k 个人的检测。
A \mathrm A A 国估计被测的民众的感染率大概是 1 1 1%,呈均匀分布。请问 k k k 取多少能最节省试剂盒?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
10
#include
int N = 10000, ans = 100;
int calc(int k) { return N / k + N / 100 * k; }
int main() {
for (int i = 1; i < 100; ++i)
if (calc(i) < calc(ans)) ans = i;
printf("%d", ans);
}
设 N N N 为民众人数,然后暴力枚举 [ 1 , 100 ) [1,100) [1,100) 这个区间就行,因为 k ≥ 100 k \geq 100 k≥100 时,每个人都要重新做一遍检测,答案必不优。
感染人数呈均匀分布,即 p { θ = 某 民 感 染 } = 1 100 p\{\theta = 某民感染\} = \frac 1{100} p{θ=某民感染}=1001、 p { θ = 某 民 健 康 } = 99 100 p\{\theta = 某民健康\} = \frac {99}{100} p{θ=某民健康}=10099,
k k k 名群众中无有感染者,可以看作为 k k k 重伯努利试验,其中有感染者的概率为 ∑ i = 1 k ( C k i ( 1 100 ) i ( 99 100 ) k − i ) \displaystyle\sum_{i=1}^k\left(C_{k}^{i}(\frac 1{100})^i(\frac {99}{100})^{k-i}\right) i=1∑k(Cki(1001)i(10099)k−i),也就说,当 k k k 取定时,只有 ( 99 100 ) k (\frac{99}{100})^k (10099)k 的分组不用重新检测,所用的试剂盒个数为 N k + N ( 1 − ( 99 100 ) k ) \frac Nk + N(1-(\frac{99}{100})^k) kN+N(1−(10099)k)。
设函数 f ( x ) = N x + N ( 1 − ( 99 100 ) x ) f(x) = \frac Nx+N(1-(\frac{99}{100})^x) f(x)=xN+N(1−(10099)x), N N N 为一个较大常数, f ′ ( x ) = − N x 2 − N ln 99 100 ( 99 100 ) x = − N ( x − 2 + ln 99 100 ( 99 100 ) x ) f'(x) = -\frac N{x^2} - N\ln\frac{99}{100}(\frac{99}{100})^x = -N(x^{-2} + \ln\frac{99}{100}(\frac{99}{100})^x) f′(x)=−x2N−Nln10099(10099)x=−N(x−2+ln10099(10099)x),大致能看出 f ( x ) f(x) f(x) 在 [ 1 , 100 ) [1,100) [1,100) 的极小值在 ( 10 , 11 ) (10,11) (10,11) 之间,可以说 10 10 10、 11 11 11 都是对的。
但赛委组怎么考虑这道题的,我就不得而知了。
本题总分: 10 10 10 分
问题描述
某市市长获得了若干批口罩,每一批口罩的数目如下:(如果你把以下文字复制到文本文件中,请务必检查复制的内容是否与文档中的一致。在试题目录下有一个文件 mask.txt,内容与下面的文本相同)
9090400
8499400
5926800
8547000
4958200
4422600
5751200
4175600
6309600
5865200
6604400
4635000
10663400
8087200
4554000
现在市长要把口罩分配给市内的 2 所医院。由于物流限制,每一批口罩只能全部分配给其中一家医院。市长希望 2 所医院获得的口罩总数之差越小越好。请你计算这个差最小是多少?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
2400
#include
int n, i, cnt, sum, ans = 0x3F3F3F3F, A[1000];
int abs(int a) { return a > 0 ? a : -a; }
int main() {
freopen("mask.txt", "r", stdin);
while (~scanf("%d", &A[n])) sum += A[n++];
for (int k = 1 << n; --k;) {
for (cnt = i = 0; i < n; ++i)
if (k >> i & 1) cnt += A[i];
if (abs(sum - 2 * cnt) < ans) ans = abs(sum - 2 * cnt);
}
printf("%d", ans);
}
n n n 比较小,可以直接枚举所有可能的分配,当 n n n 大了可以尝试在 m a p \mathrm{map} map 上做个背包 d p \mathrm{dp} dp,但写起来可能有点恶心,这里就不实现了。
本题总分: 10 10 10 分
问题描述
把 1 ∼ 2020 1 \sim 2020 1∼2020 放在 2 × 1010 2 × 1010 2×1010 的矩阵里。要求同一行中右边的比左边大,同一 列中下边的比上边的大。一共有多少种方案?
答案很大,你只需要给出方案数除以 2020 2020 2020 的余数即可。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
1340
// 等会写
将 2 × 1010 2 × 1010 2×1010 的矩阵看作杨表,则其方案数有勾长公式 dim π λ = n ! ∏ x ∈ Y ( λ ) h o o k ( x ) = 2020 ! 1011 ! 1010 ! ( m o d 2020 ) \dim \pi _\lambda=\cfrac {n!}{\prod_{x\in Y(\lambda)}{\mathrm{hook}}(x)} = \cfrac{2020!}{1011!1010!} \pmod {2020} dimπλ=∏x∈Y(λ)hook(x)n!=1011!1010!2020!(mod2020)
由于模数非质、因子较小且连续,故不采用扩展欧几里得求逆元,直接统计质因子质数再将其相乘即可。
#include
using namespace std;
map<int, int> factor[2021];
vector<int> primes;
int ans = 1;
int main() {
for (int i = 2; i <= 2020; ++i) {
if (!factor[i][0]) {
primes.push_back(i);
factor[i][i] = 1;
}
for (int p : primes) {
if (p * i > 2020) break;
factor[p * i] = factor[i];
factor[p * i][0] = 1;
factor[p * i][p]++;
if (i % p == 0) break;
}
}
for (int i = 1; i <= 1010; ++i)
for (auto &p : factor[i]) factor[2020][p.first] -= p.second;
for (int i = 1012; i < 2020; ++i)
for (auto &p : factor[i]) factor[2020][p.first] += p.second;
for (auto &p : factor[2020])
for (int i = 0; i < p.second; ++i) if (p.first) ans = ans * p.first % 2020;
printf("%d", ans);
}
上当了,应该用扩欧的。
当然,如果会点 P y t h o n \mathrm {Python} Python 的话:
ans = 1
for i in range(1, 1011):
ans = ans * (i + 1011) // i
print(ans % 2020)
本题总分: 15 15 15 分
问题描述
如果一个整数本身是完全平方数,它的每一位组成数字也是完全平方数,我们就称它是完美平方数。
前几个完美平方数是 0 、 1 、 4 、 9 、 49 、 100 、 144 ⋯ ⋯ 0、1、4、9、49、100、144\cdots\cdots 0、1、4、9、49、100、144⋯⋯
即第 1 1 1 个完美平方数是 0 0 0,第 2 2 2 个是 1 1 1,第 3 3 3 个是 4 4 4, ⋯ ⋯ \cdots\cdots ⋯⋯
那么,第 2020 2020 2020 个完美平方数是多少?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
1499441040
#include
int N = 2020, ans = 0;
bool check(int n) { return !n || n == 1 || n == 4 || n == 9; }
int main() {
while (N) {
bool flag = 1;
for (int n = ans * ans; n; n /= 10)
if(!check(n % 10)) flag = 0;
if (flag) --N;
if (N) ans++;
}
printf("%d", ans * ans);
}
设答案与 N = 2020 N = 2020 N=2020 的关系为 f ( N ) f(N) f(N),则对于几种常见的枚举对象,它们的复杂度是。
O ( f ( N ) ) O(f(N)) O(f(N)),枚举 1 ∼ f ( x ) 1\sim f(x) 1∼f(x) 中的每一个数。
O ( 4 log f ( N ) ) O(4^{\log f(N)}) O(4logf(N)),枚举 1 ∼ f ( x ) 1\sim f(x) 1∼f(x) 中的能被 0 , 1 , 4 , 9 0, 1,4, 9 0,1,4,9 组合成的数。
O ( f ( N ) ) O(\sqrt{f(N)}) O(f(N)),枚举 1 ∼ f ( x ) 1\sim f(x) 1∼f(x) 中的完全平方数。
对于第一种,它的运行耗时显然不是我们所能接受,对于第二种则需要我们对于答案的特征,如它的位数有着快速准确的判断,来避免在调试程序花费不必要的时间。
总之保持对时间复杂度的敏感程度,至少可以避免你选择到最差解。
时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 15 15 15 分
问题描述
小明有一串很长的英文字母,可能包含大写和小写。
在这串字母中,有很多连续的是重复的。小明想了一个办法将这串字母表达得更短:将连续的几个相同字母写成字母 + + + 出现次数的形式。
例如,连续的 5 5 5 个 a \mathrm a a ,即 a a a a a \mathrm{aaaaa} aaaaa ,小明可以简写成 a 5 \mathrm{a5} a5 (也可能简写成 a 4 a \mathrm{a4a} a4a 、 a a 3 a \mathrm{aa3a} aa3a 等)。
对于这个例子: H H H e l l l l l o o \mathrm{HHHellllloo} HHHellllloo ,小明可以简写成 H 3 e l 5 o 2 \mathrm{H3el5o2} H3el5o2。为了方便表达,小明不会将连续的超过 9 9 9 个相同的字符写成简写的形式。
现在给出简写后的字符串,请帮助小明还原成原来的串。
输入格式
输入一行包含一个字符串。
输出格式
输出一个字符串,表示还原后的串。
测试样例1
Input:
H3el5o2
Output:
HHHellllloo
评测用例规模与约定
对于所有评测用例,字符串由大小写英文字母和数字组成,长度不超过 100 100 100。
请注意原来的串长度可能超过 100 100 100。
#include
char s[2020];
int main() {
scanf("%s", s);
for (int i = 0; s[i]; ++i) {
if (s[i] > '9') putchar(s[i]);
else while (s[i]-- > '1') putchar(s[i - 1]);
}
}
签到。
时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 20 20 20 分
问题描述
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 1 1 至第 n n n 行,从左到右依次为第 1 1 1 至第 m m m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 1 1 行第 1 1 1 列,要走到第 n n n 行第 m m m 列。
只能向右或者向下走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。
输入格式
输入一行包含两个整数 n , m n,m n,m。
输出格式
输出一个整数,表示答案。
测试样例1
Input:
3 4
Output:
2
评测用例规模与约定
对于所有评测用例, 1 ≤ n ≤ 30 , 1 ≤ m ≤ 30 1 \leq n \leq 30, 1 \leq m \leq 30 1≤n≤30,1≤m≤30。
#include
int n, m, dp[50][50] = {0, 1};
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (i & 1 || j & 1) dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
printf("%d", dp[n][m]);
}
二连签到。
时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 20 20 20 分
问题描述
给定一个长度为 n n n 的数组 A 1 , A 2 , ⋯ , A n A_1,A_2,\cdots,A_n A1,A2,⋯,An。你可以从中选出两个数 A i A_i Ai 和 A j A_j Aj ( i i i 不等于 j j j) ,然后将 A i A_i Ai 和 A j A_j Aj 一前一后拼成一个新的整数。例如 12 12 12 和 345 345 345 可以拼成 12345 12345 12345 或 34512 34512 34512 。注意交换 A i A_i Ai 和 A j A_j Aj 的顺序被视为 2 2 2 种拼法,即便是 A i = A j A_i=A_j Ai=Aj 时。
请你计算有多少种拼法满足拼出的整数小于等于 K K K。
输入格式
第一行包含 2 2 2 个整数 n n n 和 K K K。
第二行包含 n n n 个整数 A 1 , A 2 , ⋯ , A n A_1,A_2,\cdots,A_n A1,A2,⋯,An。
输出格式
输出一个整数,表示答案。
测试样例1
Input:
4 33
1 2 3 4
Output:
8
评测用例规模与约定
对于 30 30 30% 的评测用例, 1 ≤ n ≤ 1000 , 1 ≤ K ≤ 1 0 8 , 1 ≤ A i ≤ 1 0 4 1\leq n \leq 1000, 1 \leq K \leq 10^8, 1 \leq A_i \leq 10^4 1≤n≤1000,1≤K≤108,1≤Ai≤104。
对于所有评测用例, 1 ≤ n ≤ 100000 , 1 ≤ K ≤ 1 0 10 , 1 ≤ A i ≤ 1 0 9 1\leq n \leq 100000, 1 \leq K \leq 10^{10}, 1 \leq A_i \leq 10^9 1≤n≤100000,1≤K≤1010,1≤Ai≤109。
#include
#include
long long K, ans, A[100010];
int n;
int main() {
scanf("%d %lld", &n, &K);
for (int i = 0; i < n; ++i)
scanf("%d", &A[i]);
std::sort(A, &A[n]);
for (int i = 0; i < n; ++i) {
long long k = 1;
while (A[i] >= k) k *= 10;
long long *find = std::upper_bound(A, &A[n], K / k - (K % k < A[i]));
if (find > &A[i]) --ans;
ans += find - A;
}
printf("%lld", ans);
}
签到三连。
S T L \mathrm{STL} STL 好用的想转 C \mathrm C C。
时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 25 25 25 分
问题描述
小明有 n n n 颗石子,按顺序摆成一排,他准备用胶水将这些石子粘在一起。
每颗石子有自己的重量,如果将两颗石子粘在一起,将合并成一颗新的石子,重量是这两颗石子的重量之和。
为了保证石子粘贴牢固,粘贴两颗石子所需要的胶水与两颗石子的重量乘积成正比,本题不考虑物理单位,认为所需要的胶水在数值上等于两颗石子重量的乘积。
每次合并,小明只能合并位置相邻的两颗石子,并将合并出的新石子放在原来的位置。
现在,小明想用最少的胶水将所有石子粘在一起,请帮助小明计算最少需要多少胶水。
输入格式
输入的第一行包含一个整数 n n n,表示初始时的石子数量。
第二行包含 n n n 个整数 w 1 , w 2 , … , w n w_1 , w_2 , … , w_n w1,w2,…,wn,依次表示每颗石子的重量。
输出格式
输出一行包含一个整数,表示最少需要的胶水数。
测试样例1
Input:
3
3 4 5
Output:
47
测试样例2
Input:
8
1 5 2 6 3 7 4 8
Output:
546
评测用例规模与约定
对于 20 20 20% 的评测用例, 1 ≤ n ≤ 15 1\leq n \leq 15 1≤n≤15。
对于 60 60 60% 的评测用例, 1 ≤ n ≤ 100 1\leq n \leq 100 1≤n≤100。
对于 80 80 80% 的评测用例, 1 ≤ n ≤ 1000 1\leq n \leq 1000 1≤n≤1000。
对于所有评测用例, 1 ≤ n ≤ 100000 , 1 ≤ w i ≤ 1000 1\leq n \leq 100000, 1 \leq w_i \leq 1000 1≤n≤100000,1≤wi≤1000。
#include
int n, stone, stones;
long long ans;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &stone);
ans += stone * stones;
stones += stone;
}
printf("%lld", ans);
}
怎么说呢,
我们都知道长方形面积公式 S = a × b S=a×b S=a×b,
也都知道长方形 r 1 r_1 r1、 r 2 r_2 r2 周长相等时,它们面积相同,
根据此性质我们可以得到推论,无论合并的顺序,最后一次合并需要的胶水量是相同的,也容易知道若倒数二、第三次合并存在,无论合并顺序,它们的和也是相同的,以此类推,
最终,答案等于 ∑ i = 1 n ( w i ∑ j = 1 i − 1 w j ) \displaystyle\sum_{i=1}^n(w_i\displaystyle\sum_{j=1}^{i-1}w_j) i=1∑n(wij=1∑i−1wj)。
时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 25 25 25 分
问题描述
小明正在做一个网络实验。
他设置了 n n n 台电脑,称为节点,用于收发和存储数据。
初始时,所有节点都是独立的,不存在任何连接。
小明可以通过网线将两个节点连接起来,连接后两个节点就可以互相通信了。两个节点如果存在网线连接,称为相邻。
小明有时会测试当时的网络,他会在某个节点发送一条信息,信息会发送到每个相邻的节点,之后这些节点又会转发到自己相邻的节点,直到所有直接或间接相邻的节点都收到了信息。所有发送和接收的节点都会将信息存储下来。一条信息只存储一次。
给出小明连接和测试的过程,请计算出每个节点存储信息的大小。
输入格式
输入的第一行包含两个整数 n , m n, m n,m,分别表示节点数量和操作数量。节点从 1 1 1 至 n n n 编号。
接下来 m m m 行,每行三个整数,表示一个操作。
如果操作为 1 a b 1\ a\ b 1 a b,表示将节点 a a a 和节点 b b b 通过网线连接起来。当 a = b a = b a=b 时,表示连接了一个自环,对网络没有实质影响。
如果操作为 2 p t 2\ p\ t 2 p t,表示在节点 p p p 上发送一条大小为 t t t 的信息。
输出格式
输出一行,包含 n n n 个整数,相邻整数之间用一个空格分割,依次表示进行完上述操作后节点 1 1 1 至节点 n n n 上存储信息的大小。
测试样例1
Input:
4 8
1 1 2
2 1 10
2 3 5
1 4 1
2 2 2
1 1 2
1 2 4
2 2 1
Output:
13 13 5 3
评测用例规模与约定
对于 30 30 30% 的评测用例, 1 ≤ n ≤ 20 , 1 ≤ m ≤ 100 1 ≤ n ≤ 20,1 ≤ m ≤ 100 1≤n≤20,1≤m≤100。
对于 50 50 50% 的评测用例, 1 ≤ n ≤ 100 , 1 ≤ m ≤ 1000 1 ≤ n ≤ 100,1 ≤ m ≤ 1000 1≤n≤100,1≤m≤1000。
对于 70 70 70% 的评测用例, 1 ≤ n ≤ 1000 , 1 ≤ m ≤ 10000 1 ≤ n ≤ 1000,1 ≤ m ≤ 10000 1≤n≤1000,1≤m≤10000。
对于所有评测用例, 1 ≤ n ≤ 10000 , 1 ≤ m ≤ 100000 , 1 ≤ t ≤ 100 1 ≤ n ≤ 10000,1 ≤ m ≤ 100000,1 ≤ t ≤ 100 1≤n≤10000,1≤m≤100000,1≤t≤100。
由于某个节点发出的消息,所有与其相邻或间接相邻的节点都会接收并存储一次,于是容易想到对于每个节点所在的联通块,选出一个节点代发这个联通块中所有节点发出的消息,
于是问题就转变为了,快速确定联通块的代表节点,快速合并联通块,这恰与并查集的主要功能相吻合。
对于每次发送的消息,我们都放在父节点暂存起来,最后输出每个节点储存信息大小时,对每个连通块的父节点做一遍 d f s \mathrm{dfs} dfs 下传信息,而对于未联通的块 a a a、 b b b 之间的第一次联通,若将 a a a 设为 b b b 的父节点,容易知道 a a a 原先的所有子孙节点不受影响,而 b b b 以及 b b b 的子孙节点在最后 d f s \mathrm{dfs} dfs 时,会额外的统计上此时 a a a 暂存的信息,因此在连接时需要将 b b b 暂存的信息减去 a a a 暂存的信息。
#include
#include
std::vector<int> graph[10010];
int n, m, x, y, opt, linked[10010];
long long lazy[10010];
bool visited[10010];
int find(int a) { return a == linked[a] ? a : (linked[a] = find(linked[a])); }
void dfs(int u) {
if (visited[u]) return;
visited[u] = 1;
for (int v : graph[u])
lazy[v] += lazy[u], dfs(v);
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) linked[i] = i;
while (m--) {
scanf("%d %d %d", &opt, &x, &y);
if (opt == 1) {
x = find(x), y = find(y);
if (x != y)
graph[x].push_back(y),
lazy[y] -= lazy[x],
linked[y] = x;
} else lazy[find(x)] += y;
}
for (int i = 1; i <= n; ++i)
dfs(find(i)), printf("%lld ", lazy[i]);
}
当然本质上,某个节点存储的信息量就是这个这个节点发生的信息与该节点到根节点之间 “差分” 的和,因此也可以使用带权并查集来省略 d f s \mathrm{dfs} dfs 的过程。
#include
int n, m, x, y, opt, value[10010], linked[10010];
long long lazy[10010];
int find(int a) {
if (a == linked[a]) return a;
int root = find(linked[a]);
lazy[a] += lazy[linked[a]];
return linked[a] = root;
}
void merge(int a, int b) {
a = find(a);
b = find(b);
if (a == b) return;
lazy[b] = value[b] - value[a];
linked[b] = a;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) linked[i] = i;
while (m--) {
scanf("%d %d %d", &opt, &x, &y);
if (opt == 1) merge(x, y);
else value[find(x)] += y;
}
for (int i = 1; i <= n; ++i) printf("%lld ", value[find(i)] +lazy[i]);
}
略微缩小了一点常数。
要我定难度的话,这也是道签到题。