题目描述
After solving the Basic Gcd Problem, ZYB gives you a more difficult one:
Given an integer , find two subset
and
of
such that:
and
; let
and
, there exists two permutations
and
such that for each
,
.
Please find two subsets with maximum value of .
输入描述
There are multiple test cases. The first line of input contains an integer , indicating the number of test cases.
For each test case, there is only one line containing an integer
. It's guaranteed that the sum of
of all test cases will not exceed
.
输出描述
For each test case, output an integer in the first line. In the next
lines, each contains two integers
and
, denoting the
-th element in subset
and
.
If there are multiple solutions, you can output any of them.
示例1
输入
2 4 10
输出
1 2 4 4 3 9 5 10 8 2 4 6
分析
题意描述较为复杂,不妨直接令 和
都为
的标准排列,那么问题转化为:在区间
中取出尽量多的正整数对
,满足
,且每个数字只能使用一次;将
放入序列
,
放入序列
,序列的长度为
,
,且
,有
。
显然,对于 之间的全体偶数,可以随意配对。所以我们暂且不考虑偶数,将偶数放到最后配对。
先枚举除 以外所有不超过
的质数
,对于
的质数
,不存在与其配对的数,首先将其剔除。若质数
,那么对于不大于
的
的倍数,可以组成集合
;显然,若集合
中任意两个元素都不互质;若
为偶数,那么正好可以两两配对;若
为奇数,取出
中一个偶数暂时不用(事实上,
中一定会包含
),将剩余元素两两配对。此时,一部分偶数已经配对,如
。
最后,将剩下的还没配对的偶数两两配对。
代码
/****************************************************************** Copyright: 11D_Beyonder All Rights Reserved Author: 11D_Beyonder Problem ID: 2020牛客暑期多校训练营(第四场) Problem H Date: 8/18/2020 Description: Number Theory, Construction *******************************************************************/ #include<iostream> #include<cstdio> #include<algorithm> #include<vector> using namespace std; const int N=200009; int _; int prime[N>>1];//质数 bool notprime[N]; int tot; bool used[N];//记录是否使用 vector<int>ans;//记录答案 void EulerSieve(int);//欧拉筛 int main(){ EulerSieve(N-1); for(cin>>_;_;_--){ int n; ans.clear();//清空 scanf("%d",&n); fill(used,used+n+2,0);//初始化 int i,j; for(i=2;i<=tot;i++){ //枚举除2以外的质数 if(2*prime[i]>n){ //剪枝 //不必再继续枚举更大的质数 break; } vector<int>prepared;//质数的倍数 for(j=prime[i];j<=n;j+=prime[i]){ if(!used[j]){ //未使用的p的倍数作为备选 prepared.push_back(j); } } if(prepared.size()&1){ //奇数个 //去掉其中一个的偶数 for(j=0;j<prepared.size();j++){ if(prepared[j]%2==0){ prepared.erase(prepared.begin()+j); break; } } } for(j=0;j<prepared.size();j+=2){ //两两配对 used[prepared[j]]=used[prepared[j+1]]=1; ans.push_back(prepared[j]); ans.push_back(prepared[j+1]); } } vector<int>even;//剩余未使用的偶数 for(i=1;i<=n;i++){ if(!used[i]&&i%2==0){ even.push_back(i); } } for(i=0;i<even.size();i+=2){ if(i==even.size()-1){ //若剩余偶数数量为奇数个 //会枚举到even.size()-1 //需要break break; } used[even[i]]=used[even[i+1]]=1; ans.push_back(even[i]); ans.push_back(even[i+1]); } printf("%d\n",ans.size()>>1); for(i=0;i<ans.size();i+=2){ //两两输出答案 printf("%d %d\n",ans[i],ans[i+1]); } } return 0; } void EulerSieve(int n){ int i,j; for(i=2;i<=n;i++){ if(!notprime[i]){ prime[++tot]=i; } for(j=1;j<=tot&&i*prime[j]<=n;j++){ notprime[i*prime[j]]=1; if(i%prime[j]==0){ break; } } } }