zoukankan      html  css  js  c++  java
  • 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
    ",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;
    }
    
    海到无边天作岸,山登绝顶我为峰
  • 相关阅读:
    C#如何连接wifi和指定IP
    3.4 小结
    3.3.4.5 起始与清除
    3.3.4.4 打印行
    3.3.4.3 设置字段分隔字符
    3.3.4.2 字段
    3.3.4.1 模式与操作
    3.3.4 使用 awk 重新编排字段
    3.3.3 使用 join 连接字段
    3.3.2 使用 cut 选定字段
  • 原文地址:https://www.cnblogs.com/Fiona0726/p/13368673.html
Copyright © 2011-2022 走看看