G - Array Partition
题意:给你(n)个数,让求共有多少种,将这(n)个数分成两个集合,两个集合里的各自乘积的最小公倍数是(1)
题解:我tm怎么可能会想到使用并查集,好久没用到了,就是用并查集将有公共质因数的存起来,然后就是可以知道一共有(x)个这样的乘积互质的集合数量,然后就考虑是从这(x)个中,分别拿出(1, 2, 3, ··· x-1)个放入第一个集合,然后就是(2^{x}-2),考虑到可以反转,那就得( imes 2)但是由于这种取法是会包括翻转,因为假设当拿(1)放入第(1)个集合,然后后面也算了拿(2, 3, 4, ··· x-1)放入第一个集合,所以就是正好的翻转,就不用( imes 2)了。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;
const int N = 1e6 + 99;
const ll mod = 1e9 + 7;
ll pr[N], pr_cnt;
bool vis[N];
void init() {
for (int i = 2; i < N; i++) {
if (!vis[i]) pr[++pr_cnt] = i;
for (int j = 1; i * pr[j]< N; j++) {
vis[i * pr[j]] = 1;
if (i % pr[j] == 0)break;
}
}
}
ll q_mi(ll a, ll k, ll mod) {
ll ret = 1;
ll x = a;
while (k) {
if (k & 1) (ret *= x) %= mod;
k >>= 1;
(x *= x) %= mod;
}
return ret;
}
ll a[N];
vector<ll>G[N];
ll pr_fac_cnt[N];
set<ll>se;
ll f[N];
ll Find(ll x) {return f[x] == x?x:f[x] = Find(f[x]);}
void solve() {
ll n;
cin >> n;
ll ans = 0;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
ll num = a[i];
for (ll j = 1; j <= pr_cnt && pr[j] * pr[j] <= num; j++) {
ll prime = pr[j];
if (num % prime == 0) {
G[pr[j]].push_back(i);
pr_fac_cnt[prime]++;
while ( num % prime == 0) num /= prime;
}
}
if (num != 1) {
pr_fac_cnt[num]++;
G[num].push_back(i);
}
}
for (ll i = 1; i <= n; i++)f[i] = i;
for (ll i = 1; i <= pr_cnt; i++) {
if (pr_fac_cnt[pr[i]]) {
ll u = pr[i];
for (ll i = 1; i < G[u].size(); i++) {
ll fx = Find(G[u][i-1]);
ll fy = Find(G[u][i]);
if (fx != fy) {
f[fx] = fy;
}
}
}
}
for (ll i = 1; i <= n; i++) {
se.insert(Find(i));
}
cout << (q_mi(2, se.size(), mod) - 2 + mod )%mod << endl;
se.clear();
for (ll i = 1; i < N; i++) {
G[i].resize(0);
pr_fac_cnt[i] = 0;
}
}
signed main() {
ll t = 1;
init();
ios::sync_with_stdio(0);
cin >> t;
while (t--) {
solve();
}
}