H. Haeder Gcd Problem
题意
集合A和B都是{1,2,.....,n}的子集,A∩B≠∅。问A和B最多有多少对数GCD(Ap,Bq)>1。
题解
所有gcd>1的两个数肯定是倍数关系,最小肯定是本身和2倍。越大的数能和他匹配的就会越少,为了简单,从大的数往小的数匹配。先用线性筛O(n)预处理出1-n之间的质数,然后找到小于n的质数最大,并且要有至少一个数可以和他配对。从这个质数开始往下计算,如果有偶数个可以和该质数配对的,那就直接记录下来;如果是奇数个,最简单的方法是将这个数的二倍放弃,剩下的记录下来。因为是2的倍数,肯定存在,并且方便计算。
代码
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define ft first
#define sd second
#define pii pair<int,int>
#define pll pair<ll,ll>
using namespace std;
ll st[200100];
ll prime[200100];
ll cnt;
void Prime(ll n)
{
cnt=0;
for(ll i=2;i<=n;i++){
if(!st[i]) prime[cnt++]=i;
for(int j=0;prime[j]*i<=n;j++){
st[prime[j]*i]=1;
if(i%prime[j]==0) break;
}
}
}
ll quick(int a,int b)
{
int res=1;
while(b){
if(b&1) res=(res*a);
a=(a*a);
b>>=1;
}
return res;
}
bool vis[200100];
vector<int>v[200100];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
Prime(200000);
while(t--){
memset(vis,0,sizeof(vis));
int n;
cin>>n;
for(int i=0;i<=n;i++) v[i].clear();
int k=cnt-1;
for(int i=0;i<cnt;i++){
if(prime[i]>n) {k=i-1;break;}
}
int ans=0;
for(int i=k;i>=0;i--){
int p=n/prime[i],x=0;
for(int j=1;j<=n/prime[i];j++)
if(!vis[prime[i]*j]) x++;
if(x&1){
int flag=0;
if(n/prime[i]<2) continue;
for(int j=1;j<=n/prime[i];j++){
if(!flag&&!vis[prime[i]*j]&&(prime[i]*j)%2==0) flag=1; //删掉一个2的倍数
else if(!vis[prime[i]*j]) {
vis[prime[i]*j]=1;
v[i].push_back(prime[i]*j);
}
}
}
else{
for(int j=1;j<=n/prime[i];j++)
if(!vis[prime[i]*j]) {
vis[prime[i]*j]=1;
v[i].push_back(prime[i]*j);
}
}
}
for(int i=0;i<=k;i++){
ans+=v[i].size()/2;
}
cout<<ans<<endl;
for(int i=0;i<=k;i++){
for(int j=1;j<v[i].size();j+=2){
cout<<v[i][j-1]<<' '<<v[i][j]<<endl;
}
}
}
return 0;
}

京公网安备 11010502036488号