B - ジムのトレーニングポイント / Gym Training Points Editorial by admin
Claude 4.6 Opus (Thinking)概要
各会員が \(W\) 週間で獲得するトレーニングポイントの合計を求める問題。ポイントは「来館日数 + キャンペーン期間中の来館によるボーナス」で構成される。曜日が7種類しかないことを利用し、曜日ごとにキャンペーン日数を集計することで高速に解く。
考察
重要な気づき
ポイントの分解: 会員が通算第 \(k\) 日目に来館して得るポイントは \(1 + c\)(\(c\) はその日に適用されるキャンペーン数)なので、全体のポイントを次のように分解できます:
\[\text{合計ポイント} = \underbrace{\text{来館日数(ベースポイント)}}_{\text{days\_per\_week} \times W} + \underbrace{\text{キャンペーンボーナス}}_{\text{各キャンペーン期間中の来館日数の合計}}\]
素朴なアプローチの問題
\(W\) が最大 \(10^9\) なので、通算 \(7W\) 日(最大70億日)を1日ずつシミュレーションするのは不可能です。
解決のカギ:曜日は7種類しかない
会員の来館パターンは 曜日のみ で決まります。つまり、ある会員がキャンペーン \([S_j, E_j]\) 中に何日来館するかは、「区間 \([S_j, E_j]\) に曜日 \(d\) が何回現れるか」の合計で求まります。
さらに、全キャンペーンについてまとめると: $\(\text{ボーナス} = \sum_{d \in \text{通う曜日の集合}} \text{total\_camp}[d]\)\( ここで \)\text{total_camp}[d]\( は「全キャンペーン期間を合わせたとき、曜日 \)d$ が合計何回登場するか」です。
アルゴリズム
曜日ごとのキャンペーン日数を集計:
各キャンペーン \([S_j, E_j]\) について、曜日 \(d\)(\(1 \leq d \leq 7\))が区間内に何回現れるかを計算し、total_camp[d]に加算する。
区間 \([S, E]\) で曜日 \(d\) の出現回数は、\(k \equiv d \pmod{7}\) を満たす整数 \(k\) の個数として \(O(1)\) で計算可能。各会員の回答を計算:
- ベースポイント \(= \text{days\_per\_week} \times W\)
- ボーナス \(= \sum_{d \in \text{通う曜日}} \text{total\_camp}[d]\)
- 答え \(= \text{ベースポイント} + \text{ボーナス}\)
具体例
\(L=6, R=2\) の会員は曜日 \(\{6,7,1,2\}\) に通う(週4日)。\(W=3\) なら: - ベースポイント \(= 4 \times 3 = 12\) - ボーナス \(= \text{total\_camp}[6] + \text{total\_camp}[7] + \text{total\_camp}[1] + \text{total\_camp}[2]\)
計算量
- 時間計算量: \(O(7M + 7N) = O(M + N)\)
- 各キャンペーンにつき7曜日分の計算、各会員につき最大7曜日分の加算
- 空間計算量: \(O(N + M)\)
実装のポイント
区間内の特定曜日の数え方: 曜日 \(d\) が区間 \([S, E]\) に何回現れるかは、「\([1, X]\) で \(k \bmod 7 = t\)(\(t = d \bmod 7\))を満たす \(k\) の個数」を関数化し、\(\text{cnt}(E) - \text{cnt}(S-1)\) で求める。
\(L > R\) の場合の曜日集合: 巡回区間なので
range(L, 8)とrange(1, R+1)を結合して扱う。\(W\) が大きいがベースポイントは単なる掛け算なので問題ない。キャンペーン数 \(M\) は最大 \(2 \times 10^5\) なので各曜日ごとの集計も十分高速。
ソースコード
import sys
from itertools import accumulate
def solve():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
W = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
members = []
for i in range(N):
L = int(input_data[idx]); idx += 1
R = int(input_data[idx]); idx += 1
members.append((L, R))
campaigns = []
for j in range(M):
S = int(input_data[idx]); idx += 1
E = int(input_data[idx]); idx += 1
campaigns.append((S, E))
# For each member, we need:
# 1) Base points = number_of_days_per_week * W (each visit gives 1 point)
# 2) Campaign bonus: for each campaign [S, E], count how many days in [S, E]
# the member visits the gym.
# Member i visits on days where (k-1) % 7 + 1 is in their day set.
# If L <= R: days {L, L+1, ..., R}
# If L > R: days {L, L+1, ..., 7, 1, 2, ..., R}
# For a campaign [S, E], we need to count visits of member i in that range.
# A member visits day k if day_of_week(k) is in their set.
# day_of_week(k) = (k-1) % 7 + 1, i.e., 1-indexed.
# Count of day d (1..7) in range [S, E]:
# We need count of k in [S, E] where (k-1)%7 = d-1, i.e., k ≡ d (mod 7) (with 7≡0 mapped to 7)
# Actually k mod 7: k=1->1, k=2->2, ..., k=7->0. Let's use (k-1)%7 giving 0..6 for days 1..7.
# For range [S, E], count of k where (k-1)%7 = r (r=0..6):
# This is count of k in [S, E] where k ≡ r+1 (mod 7).
# = floor((E - (r+1))/7) + 1 if r+1 adjusted to be in [S, E], else ...
# Let's use: count of integers in [S, E] ≡ t (mod 7) where t in {1,...,7}, treat 7 as 0.
# Precompute for each campaign, the count of each weekday in [S, E].
# Then for each member, sum over campaigns the count of their active weekdays.
# But N, M up to 2e5, and weekdays only 7, so we can:
# For each weekday d (1..7), compute total campaign-day count across all campaigns.
# Then for each member, bonus = sum over d in their set of total_campaign_count[d].
# count of k in [S,E] with weekday d:
# weekday d means (k-1)%7 = d-1, i.e., k ≡ d (mod 7) (where d=7 means k≡0 mod 7)
def count_weekday_in_range(S, E, d):
# count of k in [S, E] such that (k-1) % 7 == d-1
# equivalently k % 7 == d % 7
# Let t = d % 7 (so d=7 -> t=0, d=1 -> t=1, etc.)
t = d % 7
# count of k in [S, E] with k%7 == t
# = floor(E/7)*7 adjusted...
# Use: count in [1, X] with k%7==t: if t==0: floor(X/7), else floor((X-t)/7)+1 if t<=X else 0
def cnt(X):
if X <= 0:
return 0
if t == 0:
return X // 7
else:
if t > X:
return 0
return (X - t) // 7 + 1
return cnt(E) - cnt(S - 1)
# Accumulate total campaign counts per weekday
total_camp = [0] * 8 # index 1..7
for S, E in campaigns:
for d in range(1, 8):
total_camp[d] += count_weekday_in_range(S, E, d)
out = []
for L, R in members:
# days per week
if L <= R:
days_per_week = R - L + 1
day_set = range(L, R + 1)
else:
days_per_week = 7 - L + 1 + R
day_set = list(range(L, 8)) + list(range(1, R + 1))
base = days_per_week * W
bonus = 0
for d in day_set:
bonus += total_camp[d]
out.append(str(base + bonus))
sys.stdout.write('\n'.join(out) + '\n')
solve()
この解説は claude4.6opus-thinking によって生成されました。
posted:
last update: