题目大意
给定一个数N,求满足gcd(n,m)==n xor m的所有m.(1<=m<=n)
其中1<=n<=10^10
解题思路
首先,由于n很大,直接枚举m(from 1 to n)肯定会超时;
那我们就枚举gcd(n,m)了,所有可能的结果是n的约数,这一步就涉及到整数分解;有了gcd了,要求m,m一定是gcd的倍数,枚举m,判断是否满足等式,超时!!!!!
原因是找gcd的倍数的时间复杂度太高,能不能换换思路?枚举gcd的思路不可能再变了,那么能不能变一下恒等式……
百度百科(很容易理解)
根据亦或运算的性质,原等式等价于m==n xor gcd(n,m),原问题转换为找出所有的m使得m==n xor gcd(n,m),所以先将n xor gcd ,再判断亦或结果可不可以认为是m(1<=m<=n且n,m的最大公约数==gcd),满足就记录下来。
当然,整数分解时约数是1、n的可以先不考虑,最后只要n是奇数且n>1,就把n-1记录下来,证明过程也很好理解:
如果gcd(n,m)=n,则n^m=n^n=0!=gcd(n,m),约数是n不对!
如果gcd(n,m)=1, 则n^m要想等于1,根据异或运算相同为0不同为1,n为奇数且n=m-1才能满足,所以要特判一下!
不过按理来说n的约数是有1和n的,所以整数分解时i从1开始循环便可以得到所有约数,最后也不用特判1,n了。
参考代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <vector>
#include <cstring>
#include <cmath>
#include<limits.h>
#define eps 1e-8
using namespace std;
typedef long long ll;
const int maxx=INT_MAX;
const int maxn = 1e5+10;
const ll Max=1e10;
ll n,temp,fac[maxn],ans[maxn],k;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
void divide()//整数分解得到所有约数
{
k=0;
for(ll i=2;i*i<=n;i++)//约数没有1,n,最后特判一下
if(n%i==0){
fac[k++]=i;
if(i*i<n) fac[k++]=n/i;
}
return;
}
int main()
{
// freopen("input.txt","r",stdin);
int cnt=1;
while(cin>>n){
printf("Case #%d:\n",cnt++);
divide();
ll t=0;
for(ll i=0;i<k;i++){
temp=n^fac[i];//n ^ gcd 的结果temp
if(gcd(n,temp)==fac[i]&&temp<n&&temp) ans[t++]=temp;//判断temp==m?
}
if(n&1&&n>1) ans[t++]=n-1;//n为非1的奇数(1==n),特判1
sort(ans,ans+t);
cout<<t<<endl;for(ll i=0;i<t;i++) i==0?printf("%lld",ans[i]):printf(" %lld",ans[i]);puts("");
}
return 0;
}