T1
100%
枚举 L∼R 的每个数,如果 %K 的余数为 0 说明是 K 的倍数,答案增加一。
时间复杂度 O(R−L)。
T2
100%
两重循环遍历矩阵的每个格子,如果 (i,j) 有炸弹,那么用以下方式找到这个炸弹能炸到的每个格子 (x,y):
- 两重循环枚举 x 和 y,其中 x 的循环范围为 i−3∼i+3,y 的循环范围为 y−3∼y+3;
- 判断 (x,y) 是否超出矩阵边界,即需要同时满足 1≤x≤n 和 1≤y≤m;
- 判断 (x,y) 是否在 (i,j) 炸的范围内,即还需要满足 ∣x−i∣+∣y−i∣≤3,这里 ∣x∣ 表示求 x 的绝对值。
对于通过以上方式找到的每个 (x,y),将 (x,y) 的答案增加一。
时间复杂度 O(nm)。
T3
50%
两重循环枚举 i,j,将 gcd(i,N)×gcd(j,M) 加到答案中。
注意答案可能会超出 int 范围。
注意取模。
时间复杂度 O(NM(log(N)+log(M)))。
100%
将 gcd(i,N) 作为公因式提取可得:
$\gcd(i,N)\times \gcd(1,M)+\gcd(i,N)\times \gcd(2,M)+\dots+\gcd(i,N)\times \gcd(M,M)$
$=\gcd(i,N)\times (\gcd(1,M)+\gcd(2,M)+\dots+\gcd(M,M))$
=gcd(i,N)×∑j=1Mgcd(j,M)
所以我们可以先计算出 S=∑j=1Mgcd(j,M),答案就可以这样计算:
$\sum_{i=1}^N\sum_{j=1}^M(\gcd(i,N)\times \gcd(j,M))$
$=\sum_{i=1}^N(\gcd(i,N)\times \gcd(1,M)+\gcd(i,N)\times \gcd(2,M)+\dots+\gcd(i,N)\times \gcd(M,M))$
$=\sum_{i=1}^N(\gcd(i,N)\times \sum_{j=1}^M\gcd(j,M))$
=∑i=1N(gcd(i,N)×S)。
注意答案可能会超出 long long 范围。
注意过程中取模。
时间复杂度 O(Nlog(N)+Mlog(M))。
这个式子还可以继续化简,鼓励有兴趣的同学们自己试一试。
T4
40%
如果规定 C 的最小值为 X,那么为了让极差取最小值,要想办法让 Ci 取到大于等于 X 的最小值。
对于 1∼n 中的每个 i,定义 f(i,X) 为满足 Ai+Bj≥X 的最小的 Ai+Bj。
如果 X 太大,有可能出现对于某个 i,不存在任何 j 能满足 Ai+Bj≥X 的情况。因此对于 1∼n 中的每个 i,定义 MAXC(i) 为 Ai+Bj 的最大值,预先计算 Limit=min{MAXC(i)}。这样只需限制 X≤Limit 就能保证 f(i,X) 是可以正常计算的。
规定 C 的最小值为 X 时,定义 C 的极差为 Range(X)=max{f(i,X)}−X。那么答案就是 min{Range(X)}。
因为最小值一定等于某个 Ai+Bj,所以我们可以用四重循环来求答案:
- 前两重循环枚举 1≤x≤n 和 1≤y≤m,如果 Ax+By≤Limit,那么规定最小值为 Ax+By,接下来计算 Range(Ax+By);
- 第三重循环枚举 1≤i≤n;
- 第四重循环枚举 1≤j≤m,计算出 f(i,Ax+By) 用来更新 Range(Ax+By);
- 用 Range(Ax+By) 更新答案。
时间复杂度 O(n2m2)。
60%
记 Ai+Bj 的最大值为 VC=max{Ai+Bj}。显然 Limit≤VC。
记 Ai 的最大值为 VA=max{Ai}。
对于测试点#5~6,因为 Ai,Bi≤1000,所以 Limit≤2000。
同时对于 1∼n 中的两个不同下标 i 和 j,如果 Ai=Aj 那么 f(i,X)=f(j,X)。所以对于数组 A 中的相同数字,每种只需要保留一个就可以了,这样去重过后的数组 A 规模不超过 1000。
这样我们就可以用三重循环来求答案:
- 第一重循环枚举 1≤X≤Limit,表示规定最小值为 X,接下来计算 Range(X);
- 第二重循环在去重后的数组 A 中枚举下标 i;
- 第三重循环枚举 1≤j≤m,计算出 f(i,X) 用来更新 Range(X);
- 用 Range(X) 更新答案。
时间复杂度 O(mVCVA)。
结合第一部分可以拿到 60% 的分数。
100%
两重循环枚举 1≤x≤n 和 1≤y≤m,将 Ax+By 和对应的 x 一起保存到结构体数组 D 中,具体来说 D[i].C=Ax+By,D[i].X=x。按 D[i].C 从小到大的顺序对 D 进行排序。
规定 C 的最小值为 D[L].C,找出最小的 R 满足 {D[L].x,D[L+1].x,…,D[R].x} 中 1∼n 都出现过至少一次,那么 Range(D[L].C)=D[R].C−D[L].C。
维护 cnt[x] 表示当前区间内有多少个 i 满足 D[i].X=x。
区间的初始左端点 L=1,右端点 R 为最小的 R 满足 {D[1].x,D[2].x,…,D[R].x} 中 1∼n 都出现过至少一次。
当左端点从 L 变为 L+1 时,D[L].X 的出现次数减少一次,所以 cnt[D[L].X] 减一。如果 cnt[D[L].X] 因此变成 0,那么不断将右端点向右移动,直到 cnt[D[L].X]>0。其中当右端点从 R 变为 R+1 时,D[R+1].X 的出现次数增加一次,所以 cnt[D[R+1].X] 加一。
如果右端点移动后能满足 cnt[D[L].X]>0,那么用 Range(D[L+1].C)=D[R′].C−D[L+1].C 更新答案,这里的 R′ 为此时的右端点。这个过程用到了双指针算法,这部分的总时间复杂度为 O(nm)。
整个算法的时间复杂度瓶颈在于对结构体数组 D 的排序。
时间复杂度 O(nmlog(nm))。
T5
30%
小球在移动的全过程中,相对顺序是不发生变化的。可以把小球的碰撞,看成是两个小球重合时交换编号,移动的方向不变。这一点也可以通过观察样例解释发现。
这样我们只需要求出初始时每个编号对应的排名,对每个球单独计算出 t 秒后的最终位置,将最终位置再排序一次,结合编号对应的排名就可以得到每个编号的小球的最终位置。
时间复杂度 O(nlog(n))。
50%
不论有多少墙,都不影响小球的相对顺序,所以还是对每个球单独计算最终位置再排序。
对于测试点#4~5,因为最多有一个墙,所以只有初始位于墙左边且移动方向向右和初始位于墙右边且移动方向向左的球可能撞到墙,且最多因为撞墙而改变方向一次。对于这种情况,先计算撞墙的时间,再计算剩余时间反方向移动的距离,就可以算出最终位置了。
时间复杂度 O(nlog(n))。
100%
对于一个小球,如果它的左右都有墙,那么它可能会发生很多次撞墙。
将 A 和 B 都按从小到大排序,就可以用双指针的方法找到每个小球两边距离最近的墙的位置。
假设小球左边最近的墙的位置为 L,右边最近的墙的位置为 R,那么可以把它的移动过程分为三个阶段:
- 开始移动到第一次撞墙为止;
- 从第一次撞墙开始,每经过 2×(R−L) 秒,小球就会撞到另一个墙并回到第一次撞墙的位置,所以每 2×(R−L) 秒一个周期;
- 最后可能还剩一个不完整的周期。
先计算第一次撞墙的时间,对剩余时间取模 2×(R−L) 来跳过完整的周期,再计算最后剩下的那个不完整周期内的移动,就可以算出小球的最终位置。
计算一个小球的最终位置是 O(1) 的,整个算法的时间复杂度瓶颈在于排序。
时间复杂度 O(nlog(n)+mlog(m))。
T6
30%
修改次数最少可以转化成不修改的元素最多。如果不修改 Ai 和 Aj 也能让 [L,R] 成为连续区间,其中 L≤i<j≤R,那么 Aj−Ai=j−i,移项可得 Aj−j=Ai−i。所以没有被修改的元素,都满足 Ai−i 的值相同这一条件。
定义 Bi=Ai−i,定义 g(L,R) 为 {BL,BL+1,…,BR} 中出现最多的数字的出现次数。
因为可能出现 Ai<i 的情况,所以 Bi 有可能是负数,为了便于统计每种数字的出现次数,对 B 数组进行处理:先求出 B 数组中元素的最小值 minB=min{Bi},令 Bi=Bi−minB。处理过后的 Bi≥0,下文中的 Bi 也都是做过同样处理的,不再赘述。
对于每个询问,统计 {BLi,BLi+1,…,BRi} 中每个数字的出现次数,维护出现次数的最大值 g(Li,Ri),则答案为 f(Li,Ri)=Ri−Li+1−g(Li,Ri)。
时间复杂度 O(nm)。
40%
对于测试点#4,因为 ∣Ai−i∣≤500,所以 Bi 的取值范围为 0∼1000。
记 Bi 最大值为 V=max{Bi}。
预处理 S[x][i] 数组表示 {B1,B2,…,Bi} 中数字 x 出现了多少次。
对于每个询问,在 0∼V 中枚举 x,用差分的技巧可得 {BLi,BLi+1,…,BRi} 中数字 x 共出现了 S[x][Ri]−S[x][Li−1] 次,这样我们就可以维护出 g(Li,Ri) 并计算答案。
时间复杂度 O((n+m)V)。
结合第一部分可以拿到 40% 的分数。
100%
答案 f(Li,Ri)=Ri−Li+1−g(Li,Ri),因此我们只研究如何计算 g(Li,Ri),也就是 {BLi,BLi+1,…,BRi} 的众数的出现次数。
区间众数问题有多种解法,这里仅介绍一种对数组进行分块的做法。
将 B 按下标分块,块长度取 n,那么块的数量为 ⌈nn⌉≈n。
预处理 h[i][j] 表示第 i∼j 块的众数的出现次数。处理方法为对于每个 i,将第 i 块的第一个元素的下标作为左端点,让右端点逐渐向右移动,移动的同时维护每个数字的出现次数及当前众数的出现次数,当右端点为第 j 块的最后一个元素的下标时记录 h[i][j] 为当前众数的出现次数。
预处理 s[x][i] 表示前 i 块中数字 x 出现了多少次。
将区间 [Li,Ri] 分成三个部分,如下图:

那么 g(Li,Ri) 只有两种情况:
- 如果 {BLi,BLi+1,…,BRi} 的众数只出现在图中②部分,那么这种情况下 g(Li,Ri)=h[lp][rp];
- 否则,众数一定在①或③部分出现过,枚举在①或③部分出现过的数字 Bj,计算 Bj 在 {BLi,BLi+1,…,BRi} 中的出现次数。其中 Bj 在①或③部分出现的次数可以直接统计,在②部分的出现次数为 s[Bj][rp]−s[Bj][lp−1]。
对这两种情况分别计算一个最大出现次数,两者中更大的即为 g(Li,Ri)。
空间复杂度 O(nn)。
时间复杂度 O((n+m)n)。