题目:小圆前辈的数组
小圆前辈最近收到了一个长度为 n n n数组。她怀疑是不怀好意的魔女给她的陷阱,于是她对数组进行了剖析后发现了两个关键的整数 k k k和 z z z,而解读此数组只要算出的所有连续子序列中有多少满足:
1,所有数的和为
k
k
k的倍数;
2,且其和至少为
z
z
z;
这个问题难到了小圆前辈,她便把这个问题交给了你,如果你能帮她解决的话,她将奖励你一个Accept。
输入描述:
第一行只有三个整数 n , k , z n,k,z n,k,z。
第一行共n个整数 a [ 1 ] . . . a [ n ] a[1]...a[n] a[1]...a[n]。
输出描述:
一个整形数表示答案。
题解:
处理前缀和 p r e [ i ] pre[i] pre[i],由于是要找所有数和为 k k k的倍数的数,我们对于每个 p r e [ i ] m o d k pre[i]\bmod k pre[i]modk,当 p r e [ i ] ≡ p r e [ j ] ( m o d k ) pre[i]\equiv pre[j](\bmod k) pre[i]≡pre[j](modk), i < j i<j i<j时,有 ( ∑ i j a [ i ] ) = 0 (\sum_{i}^{j}a[i])=0 (∑ija[i])=0。及区间 [ l , r ] [l,r] [l,r]的和为 k k k的倍数。这就能找到满足第一个条件的区间。
要满足第二个条件,我们要将 p r e [ i ] ( m o d k ) pre[i](\bmod k) pre[i](modk)相等的放进 v e c t o r vector vector里面二分,即枚举 p r e [ i ] pre[i] pre[i],二分查找,有多少个数小于等于 p r e [ i ] + z pre[i]+z pre[i]+z ,即满足子序列和大于等于 z z z。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=1e5+5;
int n,k,z;
int a[N];
long long pre[N];
vector<ll> mk[N];
void solve()
{
cin>>n>>k>>z;
for(int i=1;i<=n;i++)
{
cin>>a[i];
pre[i]=pre[i-1]+a[i];
mk[pre[i]%k].push_back(pre[i]);
}
ll ans=0;
for(auto x:mk[0])if(x>=z)ans++;
for(int i=0;i<k;i++)
{
int l=mk[i].size(),r;
for(int j=0;j<l;j++)
{
r=lower_bound(mk[i].begin(),mk[i].end(),mk[i][j]+z)-mk[i].begin();
ans+=l-r;
}
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(nullptr);
int t=1;
// cin>>t;
while(t--)solve();
return 0;
}