ちょっと工夫が必要な感じ
問題概要
長さ 正整数列 と、正の整数 が与えられる。
数列の区間であって、総和を で割った余りと、区間内に含まれる要素の個数とが等しいものの個数を求めよ。
制約
考えたこと
結構ややこしい。落ち着いて条件を整理しよう!!
まず、0-indexed で考えることにして、数列 の累積和を としよう。このとき、問題は次のように再解釈できる。
長さ の数列 が与えられる。
- を で割った余りが に一致する
という条件を満たす の組の個数を求めよ。
ここで、もし であることが保証されるならば、話は早くて、
⇔
を満たすような を求めればよくて、map
を用いて自然に解ける。
そして、 となることがあり得たとしても、少し考えてみれば、結局、次の条件を満たす の組の個数を求めればよいことがわかる。
これを求めるためには、 に対して、
を満たす の個数を求め、その値を足していけばよい。この操作は、やはり map
を用いてできる。計算量は と評価できる。
コード
#include <bits/stdc++.h> using namespace std; // Sj - Si ≡ j - i mod K かつ j - i < K // Si - i ≡ Sj - j mod K かつ j - i < K int main() { long long N, K; cin >> N >> K; vector<long long> A(N), S(N+1, 0); for (int i = 0; i < N; i++) { cin >> A[i]; S[i+1] = S[i] + A[i]; } long long res = 0; map<long long, long long> nums; for (int j = 0; j <= N; j++) { long long rj = ((S[j] - j) % K + K) % K; res += nums[rj]; // S[j] を追加 nums[rj]++; // j - i = K となる i を削除 int i = j - (K - 1); if (i >= 0) { long long ri = ((S[i] - i) % K + K) % K; nums[ri]--; if (nums[ri] == 0) nums.erase(ri); } } cout << res << endl; }
を満たす の個数を