题解:AT_abc367_d [ABC367D] Pedometer

题目

思路

这道题的数据很大,不能暴力枚举。题目中有坑:这是一个!怎么办呢?我们可以断环为链:把整个数组复制一遍。

所以怎么计算答案呢?用 s[i] 表示前 i-1 个数的和除以 m 的余数。然后我们要找两个数 a,b ,满足 s[a] = s[b] 且 a < b 且 a + n - 1 >= b 。

但是两重循环也会超时呀!没关系,我们可以再开一个数组来维护答案。c[s[i]] 表示 max(1,i-n+1) 至 i-1 中 s[i] 出现了多少次。只是要注意:要扔掉前面太靠前的元素,然后注意在复制一遍的数组 (n+1~n*2) 里不要把 s[i] 加进去,不然计算重复了。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

int n, m;
int a[400010];
int s[400010]; // 前缀和
int c[1000010]; // 统计

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		a[i] %= m;
		a[i + n] = a[i]; // 复制
	}
	for (int i = 2; i <= 2 * n; i++)
		s[i] = (s[i - 1] + a[i - 1]) % m;
	long long ans = 0;
	for (int i = 1; i <= n; i++)
	{
		ans += c[s[i]];
		c[s[i]]++; // 加
	} 
	for (int i = n + 1; i < 2 * n; i++)
	{
		c[s[i - n]]--; // 扔
		ans += c[s[i]];
	}
	cout << ans << endl;
	return 0;
}
  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值