x

题目大意:给定一个长度为n的正整数序列.将 {1,2,...,n} 划分成两个非空集合S,T.使得 \(gcd(∏_{i∈S}a_i,∏_{i∈T}a_i)\) = 1.求划分方案数,对 \(10^9+7\)取模.

显然的性质是:有相同因数的数字们必须在一个集合里面.

所以我们用并查集来维护.最后统计分成了几个集合.答案就是\(2^{cnt} - 2\)

线性筛+并查集即可.

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;

const int N = 1e6 + 66, mod = 1e9 + 7;

int n, cnt;
int v[N], mp[N], pri[N], vis[N], las[N];
vector <int> p[N];

inline void pres_dou()
{
int i, j;
for (i = 2; i < 1e6; ++ i)
{
if (! v[i]) pri[++ cnt] = i, mp[i] = i;
for (j = 1; j <= cnt && pri[j] * i < 1e6; ++ j)
{
v[i * pri[j]] = 1;
mp[i * pri[j]] = pri[j];
if (i % pri[j] == 0) break;
}
}
return;
}

inline void dfs(int x)
{
vis[x] = 1;
for (int i = 0; i < (int)p[x].size(); ++ i) if (! vis[p[x][i]]) dfs(p[x][i]);
return;
}

inline void yhm_func()
{
int i, x; n = read();
for (i = 1; i <= cnt; ++ i) las[pri[i]] = 0;
for (i = 1; i <= n; ++ i)
{
vis[i] = 0, p[i].clear();
x = read();
while (x > 1)
{
int tmp = mp[x];
while (x % tmp == 0) x /= tmp;
if (las[tmp]) p[i].push_back(las[tmp]), p[las[tmp]].push_back(i);
las[tmp] = i;
}
}
int res = 1;
for (i = 1; i <= n; ++ i) if (! vis[i]) dfs(i), res = res * 2 % mod;
return (void)(put((res - 2 + mod) % mod));
}

signed main()
{
pres_dou();
int T = read();
while (T --) yhm_func();

return 0;
}

后记:

巧妙的思维题