Atcoder Beginner Contest 367 D题题解

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 n2105 ,所以我们考虑时间复杂度 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+1naj(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+1nak+k=1i1ak(i=j)

同时我们发现,递推到 i i i 的时候除了 i − 1 i-1 i1,其他的都增加了 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} xi1,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 m106 ,并且我们 用除以 m m m 的余数为下标来存桶 ,所以空间没有问题。

这样一来,统计答案只需要每次去找桶里下标为 ( m − t o t ) (m-tot) (mtot) 的值(也就是个数)即可。

3. 要点提示

  1. a i ≤ 1 0 9 a_i \leq 10^9 ai109 ,我们又要求和,所以要开 l o n g   l o n g long \ long long long
  2. 统计答案需要找下标为 ( m − t o t ) (m-tot) (mtot) 的个数,考虑到前缀和 t o t tot tot 可能大于 m m m ,所以要加模数再取模
  3. 往桶里记录个数的时候不要忘了对 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难度。

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值