H. Harder Gcd Problem (素数筛选 & gcd) 2020牛客暑期多校训练营(第四场)

传送门

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路:

  • 题目意思就是需要将1 ~ n 的数分成尽量大的俩序列a,b;使得gcd(a[i],b[i]) > 1.
  • 思路还是挺简单的,筛出 1 ~ n/2 的素数,并先匹配这些素数。(2虽然是素数,但不算入这些素数行列,因为2是所有偶数的因子对后面的匹配会产生影响)
  • 对于素数x,找到 x * 1,x * 3, x * 5……直到n的所有数;若这些为奇数个,那么让 x * 1与 x * 2匹配,剩下的两两匹配;若这些数为偶数个,就直接两两匹配即可。
  • 这样将1 ~ n/2的所有素数处理一遍,对于1 ~ n 的所有奇数基本就都已经被匹配了,剩下的就都是偶数了。
  • 对于剩下的偶数就随便两两匹配即可。

代码实现:

#include<stdio.h>
#include<ctype.h>
const int N = 2e5 + 5;
int t, n;
int pr[N], a[N], b[N], c[N], d[N], vis[N];
int primes[N], res;
bool st[N];
 
void get_primes(int n) //埃氏筛素数
{
    for(int i = 2; i <= n; i ++){
        if(!st[i]){
            primes[res ++ ] = i;
            pr[i] = 1;
        }
        for(int j = 0; primes[j] <= n / i; j ++){
            st[primes[j] * i] = 1;
            if(i % primes[j] == 0) break;
        }
    }
}
 
inline void read(int &x){
    char t=getchar();
    while(!isdigit(t)) t=getchar();
    for(x=t^48,t=getchar();isdigit(t);t=getchar()) x=x*10+(t^48);
}
 
 
int main()
{
    read(t);
    get_primes(N);
    while(t --){
        read(n);
        int tt1 = 0, tt2 = 0; //初始化标记数组
        for(int i = 2; i <= n; i ++) vis[i] = 0;
        
        for(int i = 2; i <= n; i ++){
            //n/2以后的素数是找不到匹配的
            if(pr[i] && i > n / 2) continue;
 
            if(pr[i] && i <= n / 2 && i > 2){
                int tt3 = 0, tmp = n / i;
                //先匹配素数i和奇数(此奇数正好是素数的倍数)
                for(int j = 1; j <= tmp; j += 2)
                    if(!vis[i * j]) c[++ tt3] = i * j, vis[i * j] = 1;
                //因为是从前往后寻找,所有一般i * 2不会被占用
                if(tt3 % 2 && !vis[i * 2]){
                    a[++ tt1] = i;
                    b[tt1] = i * 2;
                    vis[i * 2] = 1;
                    for(int j = 2; j < tt3; j += 2)
                         a[++ tt1] = c[j], b[tt1] = c[j + 1];
                }
                else{
                    for(int j = 1; j < tt3; j += 2)
                         a[++ tt1] = c[j], b[tt1] = c[j + 1];
                }
            }
            //如果是偶数且没有被匹配过
            if(!vis[i] && i % 2 == 0) d[++ tt2] = i;
        }
        //a数组的大小加上d数组的1/2就是匹配成功的对数
        printf("%d\n", tt1 + tt2 / 2);
        for(int i = 1; i <= tt1; i ++)
            printf("%d %d\n", a[i], b[i]);
        for(int i = 1; i < tt2; i += 2)
            printf("%d %d\n", d[i], d[i + 1]);
    }
 
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Satur9/article/details/107472893