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;
}
posted @ 2020-09-20 21:55  童年の波鞋  阅读(135)  评论(0编辑  收藏  举报