Harder Gcd Problem
题目描述
After solving the Basic Gcd Problem, ZYB gives you a more difficult one:
Given an integer n, find two subset A and B of {1,2,...,n} such that:
- ∣A∣=∣B∣=m and A∩B=∅
- Let A = {a1,a2,...,am} and B = {b1,b2,...,bm},there exists two permutations p1,p2,...,pm and q1,q2,...,qm such that for each 1<=i<=m,gcd(api,bqi)>1.
Please find two subsets with maximum value of m.
输入
2
4
10
输出
1
2 4
4
3 9
5 10
8 2
4 6
题目大意
给你一个序列1~n,找出尽可能多的匹配使得每个匹配值的gcd>1,输出匹配的个数和每个匹配
解题思路
每个匹配都一定是素数与其倍数或素数不同倍数之间的匹配。
易知大于n/2的素数不存在匹配。对于不大于n/2的素数采取从大到小的策略进行匹配,因为素数越大,合适的匹配越少。如果一个素数加上其倍数共有偶数个,则可以两两一组完全匹配,如果是奇数个则先把该素数的最小倍数筛除,再把剩下的数两两匹配。
Code
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
#include<utility>
using namespace std;
#define MAXN 200005
int prime[MAXN], cnt;
bool vis[MAXN];
vector<pair<int,int> > ans;
void request_primes(void){
for(int i=2;i<MAXN/2;++i){
if(vis[i]) continue;
if(i>100000) printf("%d
",i);
prime[cnt++] = i;
for(int j=1;j*i<MAXN/2;++j) vis[j*i] = true;
}
}
int main(void)
{
request_primes();
int T; scanf("%d",&T);
while(T--){
int n; scanf("%d",&n);
memset(vis,false,(n+1)*sizeof(bool));
ans.clear();
int u = upper_bound(prime,prime+cnt,n/2)-prime;
u = min(u,cnt-1); //pass:如果n/2大于最大的素数,直接取最大的素数
for(int i=u;i>=0;--i){
vector<int> vec; vec.clear();
for(int j=2;j*prime[i]<=n;++j)
if(!vis[j*prime[i]]) vec.push_back(j*prime[i]);
int num = vec.size();
if(num&1){
ans.push_back(make_pair(prime[i],vec[0]));
vis[prime[i]] = vis[vec[0]] = true;
for(int j=1;j<num;j+=2){
ans.push_back(make_pair(vec[j],vec[j+1]));
vis[vec[j]] = vis[vec[j+1]] = true;
}
}
else if(num){
ans.push_back(make_pair(prime[i],vec[1]));
vis[prime[i]] = vis[vec[1]] = true;
for(int j=2;j<num;j+=2){
ans.push_back(make_pair(vec[j],vec[j+1]));
vis[vec[j]] = vis[vec[j+1]] = true;
}
}
}
int count = ans.size();
printf("%d
",count);
for(int i=0;i<count;++i) printf("%d %d
",ans[i].first,ans[i].second);
}
return 0;
}