题目连接c++
打表+找规律算法
时间复杂度O(logN)
less
1.题意说的是给定你n位的二进制串,除了成对的(就是指那些1的个数相同或0的个数相同的),那些不成对的数有几个。好比n为3时,能够有000,001,010,011,100,101,110,111这八种二进制数,其中001能够与010配对,011能够与110配对,剩余的没法再配对,因此最后输出4。spa
2.看要求的数的范围2<=N<=10^18
,很是大,因此说不可能暴力去作,必定存在某种规律。code
3.既然成对的就是指二进制串中1的个数相同,那么咱们能够用组合数的知识来解决。即从n位数中挑m位为1,看这样的数有几个,若为偶数,则说明没有不成对的,不然说明有落单的,这时加1便可。总结得出下列公式:blog
4.公式有了,问题来了,n这么大怎么算。对于这种题,很大可能性说明存在某种规律,如何找到规律,就须要打表实现。能够根据下列代码来打表。ci
#include<bits/stdc++.h> using namespace std; typedef long long ll; /*打表*/ const int N = 1000; ll c[N][N]; int n; ll res[N]; void init() { for(int i = 0; i < N;i ++) for(int j = 0; j <= i; j++) { if(!j) c[i][j] = 1; else c[i][j] = (c[i-1][j] + c[i-1][j-1]); } } int main() { init(); for(int i = 2; i < N; i++) { for(int j = 0; j <= i; j++) { res[i] += (c[i][j] % 2); } cout << "res[" << i << "] = " << res[i] << endl; } }
因为没有取余致使后面的数溢出变成负数了,不过没有关系,咱们只须要看前面几个数就能找到规律。get
看上面这张图,仔细观察颜色相同的下划线标注的位置。好像成2的倍数的数他们的结果相同。好像还有点什么,那么咱们就把每一个数拆分红二进制数,找到他们在输出中的位置,仔细观察。数学
将上图中二进制数对应的结果进行比对,再与二进制数自己的特征加以比较,发现最终的结果与n对应的二进制数中的1的个数有关。由此,得出了最终规律。it
5.总结一下,规律为n对应的二进制数中1的个数k,答案为2^k
。
#include<bits/stdc++.h> using namespace std; typedef long long ll; /*打表 const int N = 1000; ll c[N][N]; int n; ll res[N]; void init() { for(int i = 0; i < N;i ++) for(int j = 0; j <= i; j++) { if(!j) c[i][j] = 1; else c[i][j] = (c[i-1][j] + c[i-1][j-1]); } } int main() { init(); for(int i = 2; i < N; i++) { for(int j = 0; j <= i; j++) { res[i] += (c[i][j] % 2); } cout << "res[" << i << "] = " << res[i] << endl; } } */ ll n; int main() { cin >> n; ll res = 1; while(n) { if(n & 1) res <<= 1; n >>= 1; } cout << res ; return 0; }
代码中求组合数的模板来源于yxc大佬的数学知识模板