b_lc_使数组和能被 P 整除 & K倍区间(map记录tar_mod的位置 | 公式转换)
使数组和能被 P 整除
请你移除 最短 子数组(可以为 空),使得剩余元素的 和 能被 p 整除。 不允许 将整个数组都移除。
思路
假设,s为整个数组的和,s%p为k:
- 如果 k=0,什么都不用移除就,返回0
- 否则,我们需要找一段子数组 sub,如图
typedef long long ll;
class Solution {
public:
int minSubarray(vector<int>& A, int p) {
ll n=A.size(), s=0;
for (int v : A) s=(s+v)%p;
ll k=s%p;
if (k==0) return 0;
ll cur=0, ans=n+5;
unordered_map<ll, ll> m;
m[0]=-1;
for (int i=0; i<n; i++) {
cur+=A[i];
ll cur_mod=cur%p, tar_mod=(cur_mod-k+p)%p;
if (m.find(tar_mod)!=m.end()) {
ans=min(ans, i-m[tar_mod]);
}
m[cur_mod]=i;
}
return ans==n ? -1:ans;
}
};
K倍区间
如果其中一段连续的子序列 Ai,Ai+1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。
求出数列中总共有多少个 K 倍区间.
思路
\((s[i]-s[j])%k=0\),则s[i]%k=s[j]%k,我们要找k倍区间就是在找每一种%k前缀和的个数;
注:在更新每一种前缀和%k的时机是延迟计数,不然会导致计数错误
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll s[N], m[N];
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
ll n,k,x,ans=0; cin>>n>>k;
for (int i=1; i<=n; i++) cin>>x, s[i]=(s[i-1]+x)%k;
for (int i=1; i<=n; i++) {
if (m[s[i]]>0) ans+=m[s[i]];
m[s[i]]++;
}
cout<<ans+m[0];
return 0;
}