面白かった!
問題概要
正の整数からなる長さ の数列
が与えられる。
の値を求めよ。
制約
考えたこと
まず、 という制約が怪しい!!!
きっと、 として
な計算量になるに違いないと思えた。
とりあえず、数列を元のまま考えるのではなく、度数分布表を作ることにした。つまり、次の配列を考えることにした。
num[v]
← 数列 の中の整数値
の個数
まず であるような
については別に数えることにする。このとき、
に対して、次の値の総和を求めて、
num[d]
をかけた値を求めていけばよいことがわかる。
- 値が
であるものの、数列の中での個数
- 値が
であるものの、数列の中での個数
- 値が
であるものの、数列の中での個数
- ...
そしてこの値を について合算していけばよい。
最後に計算量を見積もる。各 について、上記の区間
の個数は
個あるため、全体の計算量は
と評価できる
コード
ここでは、上記の値の代わりに、
- 値が
であるものの、数列の中での個数
- 値が
であるものの、数列の中での個数
- 値が
であるものの、数列の中での個数
- ...
の総和を求めるようにした。
#include <bits/stdc++.h> using namespace std; int main() { const int MAX = 1100000; long long N; cin >> N; vector<long long> A(N), num(MAX, 0), sum(MAX + 1, 0); for (int i = 0; i < N; ++i) { cin >> A[i]; num[A[i]]++; } for (int i = 0; i < MAX; ++i) sum[i + 1] = sum[i] + num[i]; long long res = 0; for (int i = 1; i < MAX; ++i) { if (num[i] == 0) continue; // (i, MAX), [2i, MAX), [3i, MAX), ... の総和 res += (sum.back() - sum[i+1]) * num[i]; for (int j = i*2; j <= MAX; j += i) { res += (sum.back() - sum[j]) * num[i]; } } // 等しいところは別途処理 for (int i = 1; i < MAX; ++i) { res += num[i] * (num[i] - 1) / 2; } cout << res << endl; }