题目描述
输入描述:
输出描述:
示例1
输入
2
4
10
输出
1
2 4
4
3 9
5 10
8 2
4 6
题目大意
给定一个n,求1~n中有多少对匹配。
匹配的条件是不互质。
CF上有原题……(尽管我没有做过)
分析
亿个想法
第一秒就想到1肯定是排除,因为在互质的概念里,1简直就是个bug。(de不掉的bug)
首先肯定是直觉想到质数。然后想到质数的平方肯定是和质数匹配是最优的。然后傻掉了,一堆人在那边研究一个100%TLE的代码,最后还是WA了,然后手动算了100的结果,最后发现是SPJ,真是去世……
研究过程中,发现可以把偶数先扔掉,因为偶数和偶数是必然能匹配的。
结合一下
偶数可以看做是2的倍数,也就是素数的倍数。而素数的平方也就是素数的素数倍。由此,大胆地猜想,素数和素数的倍数匹配是最优的。
手动算算
可以发现,7和11是没有匹配的,所以当该素数的倍数在≤n的情况下,只有它本身的话是不匹配的。
然后考虑从5开始还是从2开始。
显然,如果2的匹配有剩余,那么在后续中更大的素数匹配时是很难把2的倍数匹配掉。而相反的,如果5的匹配有剩余,2和3可以轻易地将剩余的匹配掉,不重不漏。
但是也有可能出现素数倍数个数是奇数的情况,此时就出现了不能匹配完,此时要把2倍的剩下来,因为它可以和偶数匹配,所有的素数中,2的倍数是最多的,因此可以最大限度地解决剩余的问题。
综述策略
把小于等于n的素数筛出来,然后从大到小去求所有素数的倍数,然后两两匹配,若是奇数个,则将2倍的剩余。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2e5+10;
struct node{
int ans1,ans2;
node(){}
node(int a1,int a2){
ans1=a1,ans2=a2;
}
};
int a[MAXN];
bool pr[MAXN],vis[MAXN];//pr 筛素数 vis 是否已匹配
vector<node> ans;//存储匹配
int main()
{
for(int i=2;i<MAXN;i++)
if(!pr[i]) for(int j=i*2;j<MAXN;j+=i) pr[j]=1;//筛素数
int t,n,num;
for(scanf("%d",&t);t--;){
scanf("%d",&n);memset(vis,0,sizeof(vis));ans.clear();
for(int i=n;i>=2;i--){
if(pr[i]) continue;num=0;//如果当前这个不是素数,跳过
for(int j=i;j<=n;j+=i) if(!vis[j]) a[++num]=j;//把倍数存到一个数组里,也可以用vector
if(num<=1) continue;//如果只有本身一个,则无法匹配
if(num&1){
ans.push_back(node(a[1],a[3]));//把2倍扔掉,本身和3倍匹配
vis[a[1]]=vis[a[3]]=1;
for(int j=4;j<=num;j+=2) ans.push_back(node(a[j],a[j+1])),
vis[a[j]]=vis[a[j+1]]=1;
}
else for(int j=1;j<=num;j+=2) ans.push_back(node(a[j],a[j+1])),
vis[a[j]]=vis[a[j+1]]=1;//两两匹配
}
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++) printf("%d %d\n",ans[i].ans1,ans[i].ans2);
}
}
END
感谢。