yukicoder No.1201 お菓子配り-4

No.1201 お菓子配り-4 - yukicoder

問題

長さ n,mn, m の数列 {ai},{bj}\{ a_i \}, \{ b_j \} が与えられるので、以下を求めよ (mod109+7)\pmod{10^9+7}

i=1nj=1mk=1bj2aikbj \sum_{i=1}^{n} \sum_{j=1}^{m} \sum_{k=1}^{b_j} 2 \left\lfloor \frac{a_i k}{b_j} \right\rfloor

制約

  • 1n,m21031 \leq n, m \leq 2 \cdot 10^3
  • 0ai1080 \leq a_i \leq 10^8
  • 1bj1081 \leq b_j \leq 10^8

考察

i,ji, j を固定して以下を高速に求めることを考える。

k=1bakb \sum_{k=1}^{b} \left\lfloor \frac{a k}{b} \right\rfloor

まず切り捨ては考えにくいので、以下のように式変形する。

k=1bakb=k=1bak(akmodb)b=abk=1bk1bk=1b(akmodb) \begin{aligned} \sum_{k=1}^{b} \left\lfloor \frac{a k}{b} \right\rfloor &= \sum_{k=1}^{b} \frac{a k - (ak \bmod b)}{b} \\ &= \frac{a}{b} \sum_{k=1}^{b} k - \frac{1}{b} \sum_{k=1}^{b} (ak \bmod b) \end{aligned}

前者の \sum は簡単に b(b+1)2\frac{b(b+1)}{2} と分かるので、後者の \sum を考える。

ここで a,ba, b が互いに素であると仮定する。すると akmodbak \bmod bk=1,,bk = 1, \cdots, b0,1,,b10, 1, \cdots, b-1 を一度ずつ経由することが分かる。よって総和は b(b1)2\frac{b(b-1)}{2} となる。

一般の場合でも、 g=gcd(a,b),a=ag,b=bgg = \gcd(a, b), a' = \frac{a}{g}, b' = \frac{b}{g} とすれば、 a,ba', b' は互いに素なので以下のように変形できる。

k=1b(akmodb)=gk=1b(akmodb)=g2k=1b(akmodb)=12g2b(b1)=12b(bg) \begin{aligned} \sum_{k=1}^{b} (ak \bmod b) &= g \sum_{k=1}^{b} (a'k \bmod b') \\ &= g^2 \sum_{k=1}^{b'} (a'k \bmod b') \\ &= \frac{1}{2} g^2 b' (b' - 1) \\ &= \frac{1}{2} b (b - g) \\ \end{aligned}

以上を整理すると、以下の通り。

k=1b2akb=2abk=1bk2bk=1b(akmodb)=abb(b+1)1bb(bg)=a(b+1)(bg)=ab+ab+g \begin{aligned} \sum_{k=1}^{b} 2 \left\lfloor \frac{a k}{b} \right\rfloor &= \frac{2a}{b} \sum_{k=1}^{b} k - \frac{2}{b} \sum_{k=1}^{b} (ak \bmod b) \\ &= \frac{a}{b} \cdot b (b + 1) - \frac{1}{b} \cdot b (b - g) \\ &= a (b + 1) - (b - g) \\ &= ab + a - b + g \end{aligned}

実装例

#543046 (C++17) No.1201 お菓子配り-4 - yukicoder

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <numeric>
#include <vector>

template <int MOD>
struct ModInt { ... };

constexpr int MOD = 1000000007;
using mint = ModInt<MOD>;

using lint = long long;

void solve() {
    int n, m;
    std::cin >> n >> m;

    std::vector<lint> xs(n), ys(m);
    for (auto& x : xs) std::cin >> x;
    for (auto& y : ys) std::cin >> y;

    mint ans = 0;
    for (auto a : xs) {
        for (auto b : ys) {
            ans += a * b + a - b + std::gcd(a, b);
        }
    }

    std::cout << ans << "\n";
}
Built with Hugo
テーマ StackJimmy によって設計されています。