na_o_ysのブログ

プログラミングなど

互いに素な3つ組の総数

問題

http://www.codechef.com/LTIME13/problems/COPRIME3
整数 N (0 < N < 105) と, N 個の整数 a_0, a_1, ..., a_[n-1] (0 < a_i < 106) が与えられる.
0 < i < j < k <= N として, a_i, a_j, a_k が互いに素となるような (i, j, k) の個数を出力せよ.
(制限時間1秒)

解答

gcd(a_i, a_j, a_k) = m なる (i, j, k) の個数に対する動的計画法で解く.
dp[m] は gcd() = m なる (i, j, k) の個数を表すとして, dp[106] から順に計算していく.
dp[1] が答えとなる.
計算量オーダーは, a_i の最大値を M (=106) として, O(Mlog(M)).

#include <iostream>
#include <vector>

using namespace std;
using ll = long long;
const int MAX = 1000000;

int main(int argc, char const* argv[])
{
    int N; cin >> N;
    int cnt[MAX+10] = {};
    for (int i = 0; i < N; i++) {
        int a; cin >> a;
        cnt[a]++;
    }

    vector<ll> dp(MAX+10);
    for (int i = MAX; i; i--) {
        // 入力値のうち i で割り切れるものの個数
        int mults = 0;
        for (int j = i; j <= MAX; j += i) mults += cnt[j];

        // multsから3つ選ぶ組み合わせ (mults C 3)
        dp[i] = (ll)mults*(mults-1)*(mults-2)/6;

        // dp[i*2], dp[i*3], ... を除く
        for (int j = i*2; j <= MAX; j += i) dp[i] -= dp[j];
    }

    cout << dp[1] << endl;
    return 0;
}

この方法だと簡単にnつ組に拡張できる.