Misaki's Kiss again
Accepts: 75
Submissions: 593
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
问题描述
摩天轮后,一些朋友希望再次得到Misaki的吻,所以Misaki把他们分别编号从1到N,如果他们中有人的编号是M,而且 gcd(N,M)=N xor M,那么他以可以得到一个吻。 请帮助Misaki找到所有的M.. Note that: GCD(a,b) 表示a和b的最大公约数. AXORB 表示A异或B.
输入描述
多组测试数据,
对于每组测试数据只有一个数
N(0<N<=1010)
输出描述
第一行Case #x: 第二行一个数count表示有多少个M 第三行有count个数,按升序输出,中间一个空格,表示具体的M..
输入样例
3 5 15
输出样例
Case #1: 1 2 Case #2: 1 4 Case #3: 3 10 12 14
Hint
第三个样例:gcd(15,10)=5且(15 xor 10)=5, gcd(15,12)=3且(15 xor 12)=3,gcd(15,14)=1且(15 xor 14)=1
2.将gcd(n,m) = n ^ m(n > m) 变换为 gcd(n,n ^ k) = n ^ k ^ n = k(k是m的约数,n ^ k < m)
3.可以用sqrt(n)的时间枚举所有的k,来反求m
4.很明显,如果右面的式子成立,那么必然存在对应的m = n ^ k,也就是说,如果存在gcd(n,n ^ k) = n ^ k
^ n = k(k是m的约数,n ^ k < m),那么一定存在这样的m = n ^ k;
5.反过来,是否对于每一个gcd(n,m) = n ^ m(n > m),m一定对应于一个n ^ k呢??gcd(n,m) = p =
n ^ m ==> p ^ n = m ^ n ^ n = m ==> gcd(n,p ^ n) = p, p依旧是n的一个约数,所以枚举所有的n的约数,
就可以找到所有符合条件的m
总结:1.这个题目打表打了很长时间都没有找到规律,感觉以后再做题的时候不能一条路走到黑
2.对于数学公式,可以尝试根据公式的概念或者定义进行变换,枚举所有表示这个公式的方法;
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
vector<LL>vec;
LL n;
LL gcd(LL a,LL b)
{
if(b)return gcd(b,a % b);
return a;
}
int main()
{
int _ = 0;
while(scanf("%I64d",&n) != EOF)
{
vec.clear();
LL cnt = sqrt(n + 1) + 1;
for(LL i = 1;i <= cnt;i++)
if((n % i) == 0)
{
if((n ^ i) < n && gcd(n,n ^ i) == i)
vec.push_back(i ^ n);
LL cur = n / i;
if(cur != i && (n ^ cur) < n && gcd(n,n ^ cur) == cur)
vec.push_back(n ^ cur);
}
sort(vec.begin(),vec.end());
cnt = unique(vec.begin(),vec.end()) - vec.begin();
printf("Case #%d:\n%I64d\n",++_,cnt - 1);
if(1 >= cnt)puts("");
for(LL i = 1;i < cnt;i++)
{
if(i != cnt - 1)printf("%I64d ",vec[i]);
else printf("%I64d\n",vec[i]);
}
}
return 0;
}