Harder Gcd Problem 2020牛客多校第四场(质数筛+构造)

原题题面

给出一正整数 n n n,找到 { 1 , 2 , 3 , . . . , n } \{1,2,3,...,n\} {1,2,3,...,n}的两个子集合 A , B A,B A,B,使:
(1) ∣ A ∣ = ∣ B ∣ = m , A ∩ B = ∅ |A|=|B|=m,A∩B=∅ A=B=m,AB=
(2)令 A = { a 1 , a 2 . . . a m } , B = { b 1 , b 2 , . . . b m } A=\{a_1,a_2...a_m\},B=\{b_1,b_2,...b_m\} A={a1,a2...am},B={b1,b2,...bm},存在两种排列方式 { p 1 , p 2 , . . . p m } , { q 1 , q 2 , . . . , q m } \{p_1,p_2,...p_m\},\{q_1,q_2,...,q_m\} {p1,p2,...pm},{q1,q2,...,qm}
使得对于 ∀ i ∈ [ 1 , m ] , g c d ( a p i , b q i ) > 1 \forall i∈[1,m],gcd(a_{p_i},b_{q_i})>1 i[1,m],gcd(api,bqi)>1

输入样例

2
4
10

输出样例

1
2 4
4
3 9
5 10
8 2
4 6

题面分析

其实是个CF原题 Codeforces450E
总体思路是先找到所有小于 n n n的质因子 p i p_i pi,然后构造" p i p_i pi k ∗ p i k*p_i kpi"之类的倍数形式。
对于某一个质因子 p i p_i pi,我们最多可以找到 ⌊ n p i ⌋ \lfloor\frac{n}{p_i}\rfloor pin个它的倍数(其实还要在这个基础上去减掉已经被计算过的数字,记为 c o u n t count count)。
如果个数 c o u n t count count为偶数,那就直接两两配对完事。
如果是奇数,在配对完之后我们剩下一个数,为了保证能配对的尽量多,我们可以去找一个偶数,
把它放在另一个容器里,在其他所有质因子完成配对后,对这个容器里的那些偶数随意地两两排列即可(gcd起码也有2)。

AC代码(25ms)

质数筛的代码出现了一些问题,还需要调整…

#include 
using namespace std;
const long long MAXN=2e5;
int prime[MAXN];//素数数组
bool is_prime[MAXN+10];//is_pri[i]表示i是素数
bool vis[MAXN+10];//是否被访问
int rest[MAXN+10];//当出现奇数个的时候被拎出来的偶数
int pow_prime[MAXN+10];//n范围内p的倍数、且未被访问过
int answer[MAXN+10][3];//存答案
//返回n以内素数的个数
long long sieve(long long n)
{
    for(int i=0; i<=n; i++)
    {
        is_prime[i]=true;
        vis[i]=false;
    }
    is_prime[0]=is_prime[1]=false;//首先标记0和1不是素数
    is_prime[2]=true;//标记2是素数
    for(int i=2; i<=sqrt(n); i++)
    {
        if (is_prime[i]) //如果i是素数
        {
            for(int j=i*i; j<=n; j+=i)//所有i的倍数都不是素数
                is_prime[j]=false;
        }
    }
    long long p=0;
    for(int i=2; i<=MAXN; i++)
    {
        if (is_prime[i])
        {
            prime[++p]=i;
        }
    }
    return p;
}
void solve1()
{
    long long sum_prime=sieve(MAXN);//质数个数
    int t;
    scanf("%d", &t);
    while(t--)
    {
        memset(vis, false, sizeof(vis));//每次重置vis的访问情况
        long long n, m=0;
        scanf("%lld", &n);
        long long maxP=1;
        long long sum_rest=0;//匹配剩下的个数的总数
        for(int i=sum_prime; i>=1; i--)//找出最大的质数
        {
            if (prime[i]*2<=n)
            {
                maxP=i;
                break;
            }
        }
        for(int i=maxP; i>=1; i--)
        {
            long long p=prime[i];//质因子
            long long sum_vis=0;//计算未被访问的p的倍数的个数
            for(int j=p; j<=n; j+=p)
            {
                if (!vis[j])
                {
                    pow_prime[++sum_vis]=j;//存储p的倍数、且未被使用的
                }
            }
            if (sum_vis==1)//一个的时候无法配对
                continue;
            if (sum_vis%2==1)//奇数
            {
                int pos=1;
                for(int j=1; j<=sum_vis; j++)
                {
                    if (pow_prime[j]%2==0)//找到第一个是偶数的(貌似可以直接2*p?)
                    {
                        pos=j;
                        break;
                    }
                }
                rest[++sum_rest]=pow_prime[pos];//加入rest
                vis[pow_prime[pos]]=true;//更新访问标记
                for(int j=1; j+1<=sum_vis; j+=2)//开始配对
                {
                    m++;
                    if (j==pos)
                        j++;
                    answer[m][1]=pow_prime[j];
                    if (j+1==pos)
                        j++;
                    answer[m][2]=pow_prime[j+1];
                    vis[pow_prime[j]]=true;//更新访问标记
                    vis[pow_prime[j+1]]=true;//更新访问标记
                }
            }
            else//偶数任意配对
            {
                for(int j=1; j+1<=sum_vis; j+=2)//开始配对
                {
                    m++;
                    answer[m][1]=pow_prime[j];
                    answer[m][2]=pow_prime[j+1];
                    vis[pow_prime[j]]=true;//更新访问标记
                    vis[pow_prime[j+1]]=true;//更新访问标记
                }
            }
        }
        if (sum_rest>=2)//rest内的配对
        {
            for(int i=1; i+1<=sum_rest; i+=2)
            {
                m++;
                answer[m][1]=rest[i];
                answer[m][2]=rest[i+1];
            }
        }
        printf("%lld\n", m);
        for(int i=1; i<=m; i++)
        {
            printf("%lld %lld\n", answer[i][1], answer[i][2]);
        }
    }
}
int main()
{
//    ios_base::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    long long test_index_for_debug=1;
    char acm_local_for_debug;
    while(cin>>acm_local_for_debug)
    {
        cin.putback(acm_local_for_debug);
        if (test_index_for_debug>100)
        {
            throw runtime_error("Check the stdin!!!");
        }
        auto start_clock_for_debug=clock();
        solve1();
        auto end_clock_for_debug=clock();
        cout<<"\nTest "<<test_index_for_debug<<" successful"<<endl;
        cerr<<"Test "<<test_index_for_debug++<<" Run Time: "
            <<double(end_clock_for_debug-start_clock_for_debug)/CLOCKS_PER_SEC<<"s"<<endl;
        cout<<"--------------------------------------------------"<<endl;
    }
#else
    solve1();
#endif
    return 0;
}

后记

赛后发现质数筛和一些细节错误导致通过率0.00%
这破题真就百度之星呗
DrGilbert 2020.7.20

你可能感兴趣的:(数论,acm竞赛)