互质数对 题解
一、题目:
二、思路:
首先说明一下题面上的错误:【输入格式】中的第一行改为“第一行一个正整数\(N\)”。
然后来说一下这道题怎么做。
先说一下骗分。由于这道题数据较水,所以对于一个数\(a_i\),只需枚举与它相邻的五个数进行判断、更新答案即可。没排序做一遍、排完序再做一遍就能过去。很神奇吧!
好了,言归正传,我们来说说正解怎么做。
注意到题目中要求\(998244353\times a_i+a_j\)最小,就等价于以\(a_i\)为第一关键字、以\(a_j\)为第二关键字的有序对\((a_i,a_j)\)最小。所以一个很暴力的想法就是把\(a\)从小到大排序,然后对于一个数\(a_i\),若在\(a_{(i+1)\sim n}\)中存在与它互质的数,那么找见第一个与它互质的数\(a_j\),输出\(a_i\)、\(a_j\)即可。
考虑如何优化。我们把优化的重心放在如何能快速判断是否\(\exists j\in [i+1,n]\),使得\(gcd(a_i,a_j)=1\)上。
考虑对每个\(a_i\),找出它的所有质因数\(p_1,p_2,\ldots,p_s\)。设\(y=p_1\times p_2\times \ldots \times p_s\),开一个数组\(val\),对于\(y\)的每一因数\(d\),让\(val[d]++\)。
现在开始统计,我们统计的目标是有多少个\(j\in [i+1,n]\),满足\(a_j\)与\(a_i\)不互质,记为\(cnt\)。枚举到一个新的\(i\)时,我们先撤销\(a_i\)对\(val\)数组的影响,这样\(val\)数组始终保存的是下标为\(i+1\sim n\)的那些数的性质。考虑容斥原理,即:
- 让\(cnt\)加上与\(a_i\)至少有1个质因数相同的数的个数。
- 让\(cnt\)减去与\(a_i\)至少有2个质因数相同的数的个数。
- 让\(cnt\)加上与\(a_i\)至少有3个质因数相同的数的个数。
- ……
最后,只要\(cnt<n-i\),那么就存在与\(a_i\)互质的数。我们只需要找出来第一个与\(a_i\)互质的那个\(a_j\),输出答案。
如果所有的\(i\)都找遍了,也没有发现合法的情况,那只能输出-1了。
三、代码:
骗分骗到AC的代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define FILEIN(s) freopen(s".in", "r", stdin)
#define FILEOUT(s) freopen(s".out", "w", stdout)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int maxn = 200005;
int n, a[maxn], ans1 = 0x3f3f3f3f, ans2 = 0x3f3f3f3f;
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
inline void find(void) {
for (int i = 1; i <= n; ++ i) {
for (int j = i + 1; j <= min(i + 5, n); ++ j) {
int A = a[i], B = a[j];
if (A > B) swap(A, B);
if (gcd(A, B) == 1) {
if (A == ans1) {
ans2 = min(ans2, B);
}
else if (A < ans1) {
ans1 = A;
ans2 = B;
}
}
}
}
}
int main() {
FILEIN("cprime"); FILEOUT("cprime");
n = read();
for (int i = 1; i <= n; ++ i) a[i] = read();
find();
sort(a + 1, a + n + 1);
find();
if (ans1 == 0x3f3f3f3f) puts("-1");
else cout << ans1 << " " << ans2 << endl;
return 0;
}
正解:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define FILEIN(s) freopen(s".in", "r", stdin)
#define FILEOUT(s) freopen(s".out", "w", stdout)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int maxn = 200005, maxa = 2000005;
int n, a[maxn];
int val[maxa];
vector<int>factor[maxn];
inline void get(int x, int id) {
for (int i = 2; i * i <= x; ++ i) {
if (x % i == 0) {
factor[id].push_back(i);
while (x % i == 0) x /= i;
}
}
if (x > 1) factor[id].push_back(x);
}
inline int call(int x, int i) { return (x >> i) & 1; }
inline void divide(int id, int k) {
for (int s = 1; s < (1 << factor[id].size()); ++ s) {
int x = 1;
for (int i = 0; i < (int)factor[id].size(); ++ i) {
if (call(s, i)) x *= factor[id][i];
}
val[x] += k;
}
}
inline int count(int s) {
int res = 0;
while (s) {
res += (s & 1);
s >>= 1;
}
return res;
}
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
inline void solve(int i) {
for (int j = i + 1; j <= n; ++ j) {
if (gcd(a[i], a[j]) == 1) {
printf("%d %d\n", a[i], a[j]);
return;
}
}
}
int main() {
FILEIN("cprime"); FILEOUT("cprime");
n = read();
for (int i = 1; i <= n; ++ i) a[i] = read();
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++ i) {
int x = a[i];
get(x, i);
divide(i, 1);
}
for (int id = 1; id < n; ++ id) {
divide(id, -1);
int sum = 0;
for (int s = 1; s < (1 << factor[id].size()); ++ s) {
int y = 1;
for (int i = 0; i < (int)factor[id].size(); ++ i) {
if (call(s, i)) y *= factor[id][i];
}
if (count(s) & 1) // count(s) is an odd number
sum += val[y];
else // count(s) is an even number
sum -= val[y];
}
if (sum != n - id) { solve(id); return 0; }
}
puts("-1");
return 0;
}