1. 程式人生 > 實用技巧 >【數學 並查集】X

【數學 並查集】X

題意

給出n個數,將它們劃分成2個集合,求出gcd(第1個集合的乘積,第2個集合的乘積)=1的劃分方案有多少種。

思路

如果2個數的gcd!=1那麼它們就必須同屬1個集合。
對於每個數的質因子,將它們放在一個聯通塊中,代表含有這些質因子的數都要屬於同一個集合。
如果數字可以放在任意一個集合,答案為2n-2,所以gcd=1答案為2聯通塊的個數-2

程式碼

#include <cstdio>
#include <algorithm>

const int MOD = 1e9 + 7;
int t, n, cnt;
int prime[1000001], v[1000001], oo[1000001], fa[1000001];

int find(int x) {
	return fa[x] = fa[x] == x ? x : find(fa[x]);
}

int power(int a, int b) {
	int res = 1;
	for (; b; b >>= 1) {
		if (b & 1) res = (long long)res * a % MOD;
		a = (long long)a * a % MOD;
	}
	return res;
}

int main() {
	scanf("%d", &t);
	for (int i = 2; i <= 1000000; i++) {
		if (!v[i]) {
			v[i] = i;
			prime[++cnt] = i;
		}
		for (int j = 1; j <= cnt; j++) {
			if (prime[j] > v[i] || prime[j] > 1000000 / i) break;
			v[i * prime[j]] = prime[j];
		}
	}
	for (int a; t; t--) {
		int tot = 0;
		scanf("%d", &n);
		for (int i = 1; i <= 1000000; i++)
			fa[i] = i;
		oo[0] = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a);
			if (a == 1) tot++;
			int last = 0;
			while (a >= 2) {
				int xx = v[a];
				while (a % xx == 0) a /= xx;
				oo[++oo[0]] = xx;
				if (last) {
					int f1 = find(last), f2 = find(xx);
					fa[f2] = f1;
				}
				last = xx;
			}
		}
		std::sort(oo + 1, oo + oo[0] + 1);
		int m = std::unique(oo + 1, oo + oo[0] + 1) - (oo + 1);
		for (int i = 1; i <= m; i++)
			if (fa[oo[i]] == oo[i]) tot++;
		printf("%d\n", power(2, tot) - 2);
	}
}