题目描述
难度分:1400
输入T(≤104)表示T组数据。每组数据输入n(1≤n≤1018)。
定义popcount(x)为x二进制中的1的个数。
输出popcount(0⊕1)+popcount(1⊕2)+popcount(2⊕3)+…+popcount((n−1)⊕n)。
其中⊕表示异或。
输入样例
5
5
7
11
1
2000000000000
输出样例
8
11
19
1
3999999999987
算法
打表找规律
说来惭愧,没有总结出很好的结论,只是通过打表找到了规律。1第一次出现是在1位置,然后每隔1个位置出现一次;3第一次出现是在2位置,然后每隔3个位置出现一次;7第一次出现是在4位置,然后每隔7个位置出现一次,……
这样就发现规律了,num在start位置第一次出现,然后每offset个数会有一个。初始化num=1,start=1,offset=2,num的出现次数为1+⌊n−startoffset⌋,其中⌊.⌋表示对.向下取整。接下来倍增start和offset,自增num,直到start超过n。
复杂度分析
时间复杂度
start从1倍增到超过n,整个算法就能够结束,而对于每个start,内部的计算都是O(1)的。因此,算法整体的时间复杂度为O(log2n)。
空间复杂度
整个算法过程仅使用了有限几个变量,因此额外空间复杂度为O(1)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int T;
LL n;
int main() {
scanf("%d", &T);
while(T--) {
scanf("%lld", &n);
LL num = 1, start = 1, offset = 2, ans = 0;
while(start <= n) {
LL cnt = 1 + (n - start) / offset;
ans += cnt * num;
num++;
start <<= 1;
offset <<= 1;
}
printf("%lld\n", ans);
}
return 0;
}