Harder Gcd Problem

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\n",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\n",count);
        for(int i=0;i<count;++i) printf("%d %d\n",ans[i].first,ans[i].second);
    }
    return 0;
}
posted @ 2020-07-23 20:55  凰琊  阅读(165)  评论(0编辑  收藏  举报