Official

G - GCD Permutation Editorial by mechanicalpenciI


正整数 \(n\) に対して \(\{ -1,0,1\}\) のいずれかを返す次のような関数 \(\tilde{\mu}(n)\) を考えます。(これはメビウス関数を元に作られています。詳しくは下の補足を見てください。)

\[ \tilde{\mu}(n)= \begin{cases} 1 & (nが相異なる奇数個の素数の積で表されるとき (n=2,3,30 など) ) \\ -1 & (nが相異なる偶数個 (0 を含まない) の素数の積で表されるとき (n=6,10,210 など) ) \\ 0 & (それ以外のとき (n=1,4,8,9,12 など) ) \end{cases} \]

そしてこれは次の性質をみたしています。

\[ \sum_{d|n} \tilde{\mu}(d)= \begin{cases} 0 & (n=1 のとき) \\ 1 & (n\geq 2 のとき) \end{cases} \]

ただし、\(\displaystyle\sum_{d|n} \mu(d)\)\(n\) の約数である \(d\) について \(\mu(d)\) を足し合わせることを表しています。
次に、\(1\leq a\leq N\) , \(1\leq b\leq N\) をみたす整数 \(a,b\) および \(1\leq i\leq j\leq N\) をみたす整数の組 \((i,j)\) に対して、次の関数 \(f(a,b;i,j)\) を考えます。

\[ f(a,b;i,j)= \begin{cases} 1 & ( (i が a の倍数) かつ (p_i が b の倍数) かつ (j が a の倍数) かつ (p_j が b の倍数) のとき ) \\ 0 & ( それ以外のとき) \end{cases} \]

このとき、

\[ S=\sum_{1\leq i\leq j\leq N} \sum_{a=1}^N \sum_{b=1}^N \tilde{\mu}(a) \tilde{\mu}(b)f(a,b;i,j) \]

を考えます。各 \((i,j)\) に対して、\(GCD(i,j)=g\), \(GCD(p_i,p_j)=g'\) とすると、

\[ \sum_{a=1}^N \sum_{b=1}^N \tilde{\mu}(a) \tilde{\mu}(b)f(a,b;i,j) =\sum_{a|g}\sum_{b|g'} \tilde{\mu}(a) \tilde{\mu}(b) \]

であり、この右辺は \(g\)\(g'\) がともに \(2\) 以上のとき \(1\) 、そうでないとき \(0\) となるためこれを \(1\leq i\leq j\leq N\) で足し合わせた \(S\) は求めるべき値と一致します。

一方で、 \(num(a,b)\)\(1\leq i\leq N\) かつ ( \(i\)\(a\) の倍数 ) かつ ( \(p_i\)\(b\) の倍数 ) をみたす整数 \(i\) の個数とすると、 \(\displaystyle\sum_{1\leq i\leq j\leq N} f(a,b;i,j)=\frac{num(a,b)(num(a,b)+1)}{2}\) より、

\[ S=\sum_{a=1}^N \sum_{b=1}^N \tilde{\mu}(a) \tilde{\mu}(b) \frac{num(a,b)(num(a,b)+1)}{2} \]

と書けます。

これを愚直に計算しようとすると \(O(N^2)\) かかってしまいますが、実際には各 \(a\) について、ある\(a\)の倍数 \(i\) について \(p_i\) の約数であるような \(b\) についてだけ \(num(a,b)\) を計算すればよいです。 さらに \(\tilde{\mu}(b)\neq 0\) であるものについてだけ調べればよく、\(2\cdot 3\cdot 5\cdot 7\cdot 11\cdot 13\cdot 17=510510>200000\) より、各 \(p_i\)について そのような \(b\) の個数は高々 \(2^6-1=63\) 個です。よって計算回数は高々 \(\displaystyle\sum_{a=1}^N \left( 63\cdot \frac{N}{a}\right)\simeq 63N\log N\simeq 2\times 10^8\) 回程度であり、定数倍も重くないので十分間に合います。 よって、この問題を解く事が出来ました。

 

補足

メビウス関数 \(\mu(n)\)

\[ \mu(n)= \begin{cases} 1 & (nが相異なる偶数個 (0 を含む) の素数の積で表されるとき (n=1,6,10,210 など) ) \\ -1 & (nが相異なる奇数個の素数の積で表されるとき (n=2,3,30 など) ) \\ 0 & (それ以外のとき (n=4,8,9,12 など) ) \end{cases} \]

で定義され、

\[ \sum_{d|n}\mu(d)= \begin{cases} 1 & (n=1 のとき) \\ 0 & (n\geq 2 のとき) \end{cases} \]

が成り立ちます。

メビウス関数は約数包除を使う問題でよく出てますが、 今回は、ともに最大公約数が \(1\) でないものを調べたいため \(n\geq 2\)\(1\) となる方が都合がよく、

\[ \tilde{\mu}(n)= \begin{cases} 1-\mu (n) & (n=1のとき) \\ -\mu(n) & (n\geq 2のとき) \end{cases} \]

と定めることで、\(\displaystyle\sum_{d|n} \tilde{\mu}(d)=1-\sum_{d|n}\mu(d)\) となります。

また、\(\displaystyle\sum_{a=1}^N \sum_{b=1}^N g(a,b)^2\sim O(N^2\log N)\) であるため、 \(64\) bit 整数型で計算を行っている限り、計算中にオーバーフローをすることもありません。

c++による実装例:

#include <bits/stdc++.h>
using namespace std;

#define N 200010

int main() {
	int n;
	int p[N];

	bool pr[N];
	int mu[N];
	vector<int>m;
	vector<int>d[N];

	bool used[N];
	vector<int>cand;
	long long num[N];

	long long ans = 0;

	cin >> n;
	for (int i = 0; i < n; i++)cin >> p[i + 1];

	for (int i = 1; i <= n; i++) {
		pr[i] = true;
		mu[i] = -1;
	}
	pr[1] = false;
	mu[1] = 0;

	for (int i = 2; i <= n; i++) {
		if (pr[i]) {
			mu[i] = -mu[i];
			for (int j = 2; (i*j) <= n; j++) {
				pr[i*j] = false;
				if (j%i == 0)mu[i*j] = 0;
				else mu[i*j] = -mu[i*j];
			}
		}
		if (mu[i] != 0) {
			m.push_back(i);
			for (int j = i; j <= n; j += i)d[j].push_back(i);
		}
	}

	for (int i = 1; i <= n; i++) {
		used[i] = false;
		num[i] = 0;
	}
	for (auto &a : m) {
		for (int i = a; i <= n; i += a) {
			for (auto &j : d[p[i]]) {
				num[j]++;
				if (!used[j]) {
					used[j] = true;
					cand.push_back(j);
				}
			}
		}
		for (auto &b : cand) {
			ans += mu[a] * mu[b] * ((num[b] * (num[b] + 1)) / 2);
			num[b] = 0;
			used[b] = false;
		}
		cand.clear();
	}

	cout << ans << endl;


	return 0;
}

posted:
last update: