Atcoder Beginner Contest 367 D题题解
题目传送门 - Atcoder Beginner Contest 367 D题
1. 题意描述
有 n n n 个点组成了一个环,其中第 i i i 个点和第 i + 1 i+1 i+1 个点之间的距离为 a i a_i ai (特别的,第 n n n 个点和第 1 1 1 个点之间的距离为 a n a_n an)。
现在问你有多少对点 ( i , j ) (i,j) (i,j) ,满足 i < j i < j i<j 且从点 i i i 顺时针走到 j j j 的最少移动距离为 m m m 的倍数。
顺时针移动是每次往更大的点的编号移动。
2. 思路分析
2.1 前言花絮
前缀和会吧~
桶思想会吧~
那这道题不就AC了?(bushi)
2.2 思路分析
在接下来的分析中,我们记从 j j j 号点走到 i i i 号点的最小移动距离为 x i , j x_{i,j} xi,j
这一题中, n ≤ 2 ∗ 1 0 5 n \leq 2*10^5 n≤2∗105 ,所以我们考虑时间复杂度 O ( n ) O(n) O(n) 解决。
我们从暴力优化的角度出发,如果暴力的话,应该是同时枚举 i , j i,j i,j ,那么时间复杂度 O ( n 2 ) O(n^2) O(n2)
那么,如何优化一维呢?
我们从样例来出发说明思路:
4 3
2 1 4 3
我们先考虑,如何统计 i = 1 i=1 i=1 时的答案呢?
由于是顺时针走,所以从其他点走到 1 1 1 号点的距离 x 1 x_1 x1 应该是这样的:
从 2 2 2 号点走到 1 1 1 号点,最小移动距离 x 1 , 2 x_{1,2} x1,2 是 1 + 4 + 3 = 8 1+4+3=8 1+4+3=8;
从 3 3 3 号点走到 1 1 1 号点,最小移动距离 x 1 , 3 x_{1,3} x1,3 是 4 + 3 = 7 4+3=7 4+3=7;
从 4 4 4 号点走到 1 1 1 号点,最小移动距离 x 1 , 4 x_{1,4} x1,4 是 3 3 3 。
观察这一组式子,你发现了什么规律呢?
没错!每往后移动一个点,加数就会少最前面的那一个。
有了这个发现,我们就能写出一个式子:
从 i i i 号点走到 1 1 1 号点,最小移动距离 x 1 , i x_{1,i} x1,i 是: x 1 , i = ∑ j = i + 1 n a j ( i ≠ 1 ) x_{1,i} = \sum_{j=i+1}^{n}a_j(i \neq 1) x1,i=j=i+1∑naj(i=1)
有了这个式子,我们依靠后缀和就可以提前 O ( n ) O(n) O(n) 处理出来。
那么,我们考虑答案如何递推呢?
容易发现,递推到
2
2
2 号点的时候,可以推出来式子:
x
2
,
i
=
x
1
,
i
+
a
1
(
i
≠
1
,
2
)
x_{2,i} = x_{1,i}+a_1(i \neq 1,2)
x2,i=x1,i+a1(i=1,2)
只需要从 1 1 1 号点再走到 2 2 2 号点即可。
那么从 1 1 1 号点走到 2 2 2 号点,就不需要再绕一圈了,所以 x 2 , 1 = a 1 x_{2,1} = a_1 x2,1=a1 。
这样一来,我们很容易把这个结论推广一下:从
j
j
j 号点走到
i
i
i 号点,最小移动距离
x
i
,
j
x_{i,j}
xi,j 是:
x
i
,
j
=
∑
k
=
j
+
1
n
a
k
+
∑
k
=
1
i
−
1
a
k
(
i
≠
j
)
x_{i,j} = \sum_{k=j+1}^{n}a_k + \sum_{k=1}^{i-1}a_k(i \neq j)
xi,j=k=j+1∑nak+k=1∑i−1ak(i=j)
同时我们发现,递推到 i i i 的时候除了 i − 1 i-1 i−1,其他的都增加了 a i a_i ai 而已。如果我们暴力修改的话复杂度还是 O ( n 2 ) O(n^2) O(n2) ,所以我们干脆直接记录一个 t o t tot tot ,用来保存统一增加的值(其实就是前缀和)即可。
这一个思路非常重要,绝大多数值的分别修改直接变为统一修改,大大提高了程序运行效率。
那么,对于 x i − 1 , i x_{i-1,i} xi−1,i ,我们就不能单纯的使其等于 a i a_i ai 了。由于其他的都增加 t o t tot tot ,所以它实际上也会加 t o t tot tot ,所以就要减去 t o t tot tot 才是正确的。
2.3 优化实现
现在整体框架呼之欲出了,但是实现起来的话,还是需要 O ( n 2 ) O(n^2) O(n2) 的转移。如何优化呢?
我们发现,是 m m m 的倍数就是可以除以 m m m 余数为 0 0 0 。根据同余的同加性(具体可以参考 Zhang_xiangyou的blog -【数论】2 同余问题(同余方程 中国剩余定理 拓展欧几里得定理等)学习笔记 ),我们只需要关心 x i , j x_{i,j} xi,j 除以 m m m 的余数相同的有多少个即可。由于 m ≤ 1 0 6 m \leq 10^6 m≤106 ,并且我们 用除以 m m m 的余数为下标来存桶 ,所以空间没有问题。
这样一来,统计答案只需要每次去找桶里下标为 ( m − t o t ) (m-tot) (m−tot) 的值(也就是个数)即可。
3. 要点提示
- a i ≤ 1 0 9 a_i \leq 10^9 ai≤109 ,我们又要求和,所以要开 l o n g l o n g long \ long long long 。
- 统计答案需要找下标为 ( m − t o t ) (m-tot) (m−tot) 的个数,考虑到前缀和 t o t tot tot 可能大于 m m m ,所以要加模数再取模。
- 往桶里记录个数的时候不要忘了对 m m m 取模。
4. 代码展示
赛时代码
#include <bits/stdc++.h>
using namespace std;
long long n,m,a[200005],p[1000005],s[200005],tot,ans;
int main()
{
cin >> n >> m;
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for (int i=n;i>=1;i--)
{
s[i] = s[i+1]+a[i];
p[s[i]%m]++;
}
for (int i=1;i<=n;i++)
{
p[s[i]%m]--;
ans += p[(m-(tot%m))%m];
tot+=a[i];
p[((a[i]-tot)%m+m)%m]++;
}
cout<<ans;
return 0;
}
5. 本题小结
这是一道练习暴力优化思想好题,用到了前缀和思想,桶思想,但是都需要加以优化和变形。尤其是整体修改用变量记录的思想,在信息学奥赛中是一种很好的优化方式,但不要忘了对单个值修改时需要减去记录的变量。
总体难度适中,难度应该在普及T2到T3或提高T1难度。