题意
给出 n 个数字1 2 3 .... n ,让构造出两个集合 A,B ,满足以下条件:
- A ∩ B == ∅ and | A | == | B | == m
- (gcd(A_i,B_i) > 1)
思路
按照每个数字的最小质因子分组。
2:
3:
5:
7:
...
初始一直想的是从 2组开始,组中每两个作为一对输出,如果有奇数个数字,那么将最后一个数字除完所有的 2 ,将其放到次小质因子组中去。就这样一直遍历。然后直接找出了反例。。。
然后就想着改进,但是想出的方法都给人一种不完美的感觉。
题解
从 3 开始进行,如果当前组的数字个数为奇数,那么从 2 组中判断是否有最小数字的 2 倍,如果有那么这俩数字作为一对,其他相邻的作为一对。
就是把 2组中的数字当做备选,最后 2 组再自行匹配。
代码
#include <bits/stdc++.h>
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 10;
int vis[N],pri[N],tot;
void solve(int n)
{
for(int i=2; i<=n; i++)
{
if(!vis[i])
pri[++tot]=i;
for(int j=1; j<=tot; j++)
{
if(i*pri[j]>n)
break;
vis[i*pri[j]]=pri[j];//纪录最小的素因子
if(i%pri[j]==0)
break;
}
}
}
vector<int>vec[N];
vector<pair<int,int> >ans;
int sign[N];
int main()
{
solve(200005);
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
vec[i].clear();
ans.clear();
for(int i=2; i<=n; i++)
{
if(vis[i]==0) vis[i]=i;
sign[i]=0;
vec[vis[i]].pb(i);
}
for(int i=3;i<=n;i++)
{
if(vec[i].size()%2){
for(int j=1;j+1<vec[i].size();j+=2)
ans.pb(make_pair(vec[i][j],vec[i][j+1]));
if(sign[vec[i][0]*2]==0&&2*vec[i][0]<=n){
ans.pb(make_pair(vec[i][0],vec[i][0]*2));
sign[vec[i][0]*2]=1;
}
}
else{
for(int j=0;j+1<vec[i].size();j+=2){
ans.pb(make_pair(vec[i][j],vec[i][j+1]));
}
}
}
int pre=0;
for(int v:vec[2]){
if(sign[v]==0){
if(pre==0) pre=v;
else
{
ans.pb(make_pair(pre,v));
pre=0;
}
}
}
printf("%d
",ans.size());
for(auto now:ans){
printf("%d %d
",now.first,now.second);
}
}
return 0;
}