优美子数列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;
}
posted @ 2024-07-26 14:42  Firra3500  阅读(7)  评论(0编辑  收藏  举报