一道很好的构造题,受益匪浅。
链接:F-命运的抉择_2024牛客寒假算法基础集训营6 (nowcoder.com)
题意:
题解 (并查集 + 思维):
首先将存在1的情况特判掉,我们的数组的元素都是>= 2的,我们发现所有的数字都是<= 1e6的所以我们可以根据筛法快速质因数分解,对于任意两个数都满足一定存在相同的质因子, 我们依次处理一下数组中每一个数字的质因数,将它们的并查集数组初始化,然后再依次将每个数的质因数合并在一起,这样有交集的质因数就会全部合在一个位置上,然后我们取第一个数的其中一个质因数,拿到它的祖先,再去数组中遍历一遍,找到和同祖先的点我们就标记为Ture, 最后如果数量是n就是无解,否则就是有解。
代码:
#include <bits/stdc++.h>
#define ff first
#define ss second
// #define int long long
using namespace std;
using PII = pair<int, int>;
using ll = long long;
constexpr int N = 1e6 + 10, mod = 1e9 + 7;
constexpr int inf = 0x3f3f3f3f;
int n, m;
int a[N];
int q[N], cnt, p[N];
bool st[N], si[N];
int z[N];
int find(int x) {
if(x != z[x]) z[x] = find(z[x]);
return z[x];
}
void solve() {
scanf("%d",&n);
for(int i = 1; i <= n; i ++ ) {
scanf("%d",&a[i]);
if(a[i] < a[1]) swap(a[1], a[i]);
}
if(a[1] == 1) {
printf("%d %d\n",1,n-1);
cout << 1 << endl;
for(int i = 2; i <= n; i ++ ) printf("%d ", a[i]);
printf("\n");
return;
}
for(int i = 1; i <= n; i ++ ) {
int x = a[i];
si[i] = 0;
while(x > 1) {
int t = p[x];
z[t] = t;
while(x % t == 0) x /= t;
}
}
for(int i = 1; i <= n; i ++ ) {
int x = a[i];
int r = -1;
while(x > 1) {
int t = p[x];
if(r == -1) r = t;
else z[find(t)] = find(r);
while(x % t == 0) x /= t;
}
}
int x = a[1], aim;
int t = p[x];
aim = find(t);
for(int i = 1; i <= n; i ++ ) {
int x = a[i];
bool flag = 0;
while(x > 1) {
int t = p[x];
if(find(t) == aim) {
flag = 1;
break;
}
while(x % t == 0) x /= t;
}
if(flag) si[i] = 1;
}
cnt = 0;
for(int i = 1; i <= n; i ++ ) if(si[i]) cnt++;
if(cnt == n) puts("-1 -1");
else {
printf("%d %d\n", cnt, n - cnt);
for(int i = 1; i <= n; i ++ )
if(si[i]) printf("%d ", a[i]);
printf("\n");
for(int i = 1; i <= n; i ++ )
if(!si[i]) printf("%d ", a[i]);
printf("\n");
}
}
signed main() {
for(int i = 2; i <= 1e6; i ++ ) {
if(!st[i]) q[cnt ++] = i, p[i] = i;
for(int j = 0; q[j] <= 1e6 / i; j ++ ) {
st[i * q[j]] = 1;
p[i * q[j]] = q[j];
if(i % q[j] == 0) break;
}
}
int ts = 1;
scanf("%d",&ts);
while(ts -- )
solve();
return 0;
}