https://ac.nowcoder.com/acm/contest/5669

H - Harder Gcd Problem

 

题意:给定 n,把 1 到 n 这 n 个数两两配对,每对不互质,要求对数最多。

构造

先处理出每个质数的倍数,每个质数对应一个集合,从大到小处理这些质数,大于 n/2 的质数显然不会有能够配对的数了,所以直接忽略。从最大的质数开始,贪心地把没有配对过的两两配对,可能多出一个,则让 2*p 作为多出的那个,放进一个备用集合里。这样处理完所有集合之后,备用集合里都是 2p,则都是 2 的倍数,这些数可以两两任意配对了。

如果这样再多出一个数,就必定只能多出来了,因为这种做法已经把所有能够配对的都用到了,只剩一个数是绝对无法再使用的了。

至于要从大到小遍历质数,则是因为最终备用集合里的数gcd都是2,也就相当于多出来的这些数都有共同因子 2 接着,如果从小到大遍历,则要从 3 开始,这样才能保证所有多出来的数都能有共同因子 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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int N = 2e6 + 10;
int T;
int prime[N];
bool is_prime[N];
vector<int>vc[N], rs;
int sieve(int n) {
int p = 0;
for (int i = 2; i <= n; i++)is_prime[i] = true, vc[i].clear();
is_prime[0] = is_prime[1] = false;
for (int i = 2; i <= n; i++) {
if (is_prime[i]) {
prime[p++] = i;
vc[i].push_back(i);
for (int j = 2 * i; j <= n; j += i) {
is_prime[j] = false;
vc[i].push_back(j);
}
}
}
return p;
}
int vis[N];
typedef pair<int, int>pii;
vector<pii>ans;
int main() {
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
sieve(n);
fill(vis, vis + n + 1, 0);
ans.clear();
rs.clear();
int a, b;
for (int i = n / 2; i >= 3; i--) {
if ((int)vc[i].size() <= 1)continue;
a = -1, b = -1;
int cnt = 0;
for (int u : vc[i]) {
if (!vis[u])cnt++;
}
if (cnt & 1) {
vis[2 * i] = 1;
rs.push_back(2 * i);
}
for (int u : vc[i]) {
if (vis[u])continue;
vis[u] = 1;
if (a == -1)a = u;
else if (b == -1)b = u;
if (a != -1 && b != -1) {
ans.push_back(pii(a, b));
a = b = -1;
}
}
}
for (int u : vc[2])if (!vis[u])rs.push_back(u);
a = -1, b = -1;
for (int u : rs) {
if (a == -1)a = u;
else if (b == -1)b = u;
if (a != -1 && b != -1) {
ans.push_back(pii(a, b));
a = b = -1;
}
}
printf("%d\n", (int)ans.size());
for (pii p : ans)printf("%d %d\n", p.first, p.second);
}
return 0;
}