问题 G: 集合划分(并查集运用+数论知识)
题目描述
给定一个长度为 n 的正整数序列 {ai }。
将 {1,2,…,n} 划分成两个非空集合 S,T,使得 gcd(Π i∈S ai, Π j∈T aj)=1。
求划分方案数,对10 9+7 取模。
将 {1,2,…,n} 划分成两个非空集合 S,T,使得 gcd(Π i∈S ai, Π j∈T aj)=1。
求划分方案数,对10 9+7 取模。
输入
第一行,一个非负整数t,代表数据组数。
每组数据的第一行,一个正整数n。
第二行,n个正整数,代表ai。
每组数据的第一行,一个正整数n。
第二行,n个正整数,代表ai。
输出
输出t行,每行一个非负整数,代表答案对10
9+7取模的结果。
样例输入 Copy
3
3
2 3 1
3
2 3 6
4
2 3 6 1
样例输出 Copy
6
0
2
提示
对于100%的数据,n≤10
5,ai≤10
6,t≤5。
思路:题意很明显,但是不好写。我先TLE,再MLE。
后来,是直接把每个素数因子出现的id推进去,再并查集合并。
看代码吧。
#pragma GCC optimize(4)
#include <bits/stdc++.h>
#define rep(i , a , b) for(register int i=(a);i<=(b);i++)
#define per(i , a , b) for(register int i=(a);i>=(b);i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pi;
typedef unordered_map<int,int> un_map;
template<class T>
inline void read (T &x) {
x = 0;
int sign = 1;
char c = getchar ();
while (c < '0' || c > '9') {
if ( c == '-' ) sign = - 1;
c = getchar ();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar ();
}
x = x * sign;
}
const int maxn = 1e6 + 10;
const int inf = int (1e9);
const ll INF = ll(1e18);
const int mod = 1e9+7;
int n;
int vis[maxn],id[maxn];
int prime[78500],x;
std::vector<int> v[100005];
int fa[100005];
int Find(int u) {
if(u==fa[u]) return fa[u];
return fa[u] = Find(fa[u]);
}
void oulasai(){
rep(i,2,maxn-5){
if(!vis[i]) prime[++x]=i,vis[i]=i,id[i]=x;
rep(j,1,x){
if(i*prime[j]>maxn-5) break;
vis[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
}
ll qpow(ll a,ll b) {
ll ans = 1;
while(b) {
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
bool visit[100005];
void solve(){
memset(visit,0,sizeof(visit));
read(n);
int cnt = 0;
rep (i,1,n) {
fa[i]=i;
int a;
read(a);
int m = a;
while (m > 1){
v[id[vis[m]]].push_back(i);
int y = vis[m];
while (m % y == 0) m /= y;
}
}
rep(i,1,x) {
int siz = v[i].size();
rep(j,1,siz-1) {
int uu = Find(v[i][j - 1]);
int vv = Find(v[i][j]);
if (uu != vv) fa[uu] = vv;
}
}
rep(i,1,n) {
int tmp = Find(i);
if(!visit[tmp]) {
cnt++;
visit[tmp]=1;
}
}
ll ans = (qpow(2,cnt)-2+mod)%mod;
printf("%lld\n",ans);
rep(i,1,x) v[i].clear();
}
int main () {
//freopen("A.txt","r",stdin);
oulasai();
int t;
scanf("%d",&t);
while(t--) {
solve();
}
//cout<<"over"<<endl;
return 0;
}