【补题】ACPC Kickoff 2025 F. Kinan The Bank Robber
题意:给出长度为n的序列,接下来给出了两个包裹,你可以选择把数字放进这两个包裹当中,要求你放的的方式,最终会让包裹内的数字双双互质,请你给出你的放法,如果没有输出-1
思路:
1.包裹内数字双双互质,其实也就是一个包裹当中,质因数只能出现一次,因数太多了不讨论,枚举一定n方,讨论gcd只看质因数是常见手段了。
2.两个包裹,并且要求所有的数都要放入,如果贪心的想,那其实就是没有相同质因数放一起就行了,但是
5
6 15 77 35 143
就hack了,答案为1 2 2 1 1,如果你也是走上了贪心的歪路,看一下这个样例就知道了
3.贪心不行,又要讨论可行的分组,分两组,其实就是二分图染色!对于每个点有相同质因数的点染上相反的颜色就行了。
代码: 对于一些好的用法进行了注释,值得学习的方法。
#include <bits/stdc++.h>
#define int long long
#define int128 __int128
#define IOS \std::ios::sync_with_stdio(0); \std::cin.tie(0); \std::cout.tie(0);
const int N = 1e7 + 10;
const int INF = 1e18;
const int MOD = 998244353;int MinP[N];void init() {for(int i = 2; i <= N; i++) {if(MinP[i] == 0) {MinP[i] = i;for(int j = i * 2; j <= N; j += i) {if(!MinP[j])MinP[j] = i;}}}
}void solve() {int n;std::cin >> n;std::vector<int> ve(n);for(int i = 0; i < n; i++) {std::cin >> ve[i];}std::vector<std::vector<int>> mp(n);std::map<int, std::vector<int>> pos;for(int i = 0; i < n; i++) {int x = ve[i];while(x > 1) {int p = MinP[x]; //MinP预处理每个数的最小质因数pos[p].push_back(i); //分解质因数,这种该方式分解质因数是log(x)while(x % p == 0)x /= p;}}for(auto& kv : pos) {auto& v = kv.second; //v相当于直接对kv.second操作,引用if(v.size() >= 3) {std::cout << -1 << '\n'; //3个相同质因数肯定不行return;} else if(v.size() > 1) {mp[v[0]].push_back(v[1]);mp[v[1]].push_back(v[0]); //存边}}std::vector<int> col(n, -1);std::queue<int> q;for(int i = 0; i < n; i++) { //BFS染色if(col[i] != -1)continue;q.push(i);col[i] = 0;while(q.size()) {auto t = q.front();q.pop();for(auto u : mp[t]) {if(col[u] == -1) {col[u] = col[t] ^ 1;q.push(u);} else if(col[u] == col[t]) {std::cout << -1 << '\n';return;}}}}for(int i = 0; i < n; i++)std::cout << (col[i] == 0 ? 1 : 2) << " \n"[i == n - 1];//" \n"[i == n - 1]代表i==n-1的时候是换行,其他时候都输出" "
}signed main() {IOS;init();int t = 1;// std::cin >> t;while(t--) {solve();}
}