优美子数列2 题解
题目id:8628
题目描述
数学家小\(Q\)得到了一个长度为\(N\)的数列\(a_n\)。
小\(Q\)的幸运数字是\(k\),所以他认为,若一个子数列\(a_l,a_{l+1},...,a_r\)的和为\(k\)的倍数,则该子数列是优美子数列。
小\(Q\)现在想考考你,\(a_n\)里有多少个优美子数列呢?
前置知识
前缀和、桶
解题思路
因为是求某段区间的和,立马就能想到前缀和,可以\(O(1)\)求出\(a_l\sim a_r\)的和:
现在问题来了,\(n\)的范围不允许我们枚举\(\left[l,r\right](1\leq n\leq 2×10^5)\),若枚举,则时间复杂度为\(O(4×10^{10})\),绝对超时,那我们该如何枚举\(\left[l,r\right]\)呢\(?\)
为此,我们需要引入一个概念\(——\)桶。
桶,意义如其名,是用来存放各种数据的,那么桶在这道题目里有什么用呢?
现在有一个用来存放前缀和数组\((\)其实是变量啦\()\)\(\pmod k\)的余数的桶数组\(t\)。
Tip:由于\(a\)数组有可能全是负数,所以我们对负数取模需要用到一个特殊公式:\(a \bmod k=(a \bmod k+k)\bmod k\)
我们假设\((a \bmod k+k)\bmod k=y\)
如果\(y\)为\(0\),则\(ans\)直接加上当前余数为\(0\)的方案数\((\)又多了这么多组合\()\),也就是\(t_{0}\)
如果\(y\)不为\(0\),则\(ans\)直接加上当前余数为\(y\)的方案数\((\)两者中和\(y\)为\(0)\),也就是\(t_{y}\)
综上所述,对于每一项,得出公式如下:
ans+=t[(s[i]%k+k)%k]++;
另外,由于\(0\pmod k=0\),所以\(t_0=1\)
其中\(s_i\)为第\(i\)项的前缀和\((s\)也可以用变量啦\()\)
最后输出\(ans\)即可。
AC Code
#include<bits/stdc++.h>
#define Ios ios::sync_with_stdio(0),cin.tie(nullptr),cout.tie(nullptr)
#define ll long long
using namespace std;
ll n,k,ans,s,sum[500005],b[500005]={1};
int main()
{
Ios,cin>>n>>k;
for(ll i=1,a;i<=n;++i)cin>>a,s=((s+a)%k+k)%k,ans+=b[s]++;
cout<<ans;
return 0;
}