Harder Gcd Problem
阿新 • • 發佈:2020-07-23
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
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; }