Harder Gcd Problem 2020牛客多校第四場(質數篩+構造)

原題題面

給出一正整數 n n ,找到 { 1 , 2 , 3 , . . . , n } \{1,2,3,...,n\} 的兩個子集合 A , B A,B ,使:
(1) A = B = m , A B = |A|=|B|=m,A∩B=∅
(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\} ,存在兩種排列方式 { p 1 , p 2 , . . . p m } , { q 1 , q 2 , . . . , q m } \{p_1,p_2,...p_m\},\{q_1,q_2,...,q_m\}
使得對於 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 html

輸入樣例

2
4
10ios

輸出樣例

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

題面分析

實際上是個CF原題 Codeforces450E
整體思路是先找到全部小於 n n 的質因子 p i p_i ,而後構造" p i p_i k p i k*p_i "之類的倍數形式。
對於某一個質因子 p i p_i ,咱們最多能夠找到 n p i \lfloor\frac{n}{p_i}\rfloor 個它的倍數(其實還要在這個基礎上去減掉已經被計算過的數字,記爲 c o u n t count )。
若是個數 c o u n t count 爲偶數,那就直接兩兩配對完事。
若是是奇數,在配對完以後咱們剩下一個數,爲了保證能配對的儘可能多,咱們能夠去找一個偶數,
把它放在另外一個容器裏,在其餘全部質因子完成配對後,對這個容器裏的那些偶數隨意地兩兩排列便可(gcd起碼也有2)。web

AC代碼(25ms)

質數篩的代碼出現了一些問題,還須要調整…數組

#include <bits/stdc++.h>
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.20app