题解 UVa11645
题目大意 多组数据,每组数据给定一个正整数 \(n\),请求出 \(\sum_{i=0}^n A(i)\),其中 \(A(n)\) 为 \(n\) 的二进制表示中连续两个 \(1\) 出现的个数(比如 \(A(6_{10})=A(110_2)=2,A(15_{10})=A(1111_2)=3\)),输入以负整数结尾。
分析 正面统计很难做,所以我们从反面来统计每组相邻的 \(1\) 会给答案做出多少贡献。不难发现,这两位做出的贡献就是其在二进制表示下前面的部分加上一个零头。而这个零头仅在这两位本来就是 \(1\) 的时候才会出现,且就是这两位后面的部分(比如 \(11101_2\) 中第 \(3,4\) 两位的贡献为 \((11000+0)_2\),\(2,3\) 位的贡献为 \((10000+01)_2\))。
比较恶心的是这道题输入不是以 \(-1\) 结尾,而是负整数,且最大的数据会爆 long long,只能手写高精度或使用 __int128
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
__int128 n, x, now, ans;
__int128 Read()
{
__int128 x = 0, op = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') op = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
return x * op;
}
void Print(__int128 x)
{
if(x < 0) putchar('-1'), x = -x;
if(x > 9) Print(x / 10);
putchar(x % 10 + '0');
}
int main()
{
while((n = Read()) >= 0) {
ans = 0, now = 0, x = n;
while(x) {
ans += ((__int128)1 << now) * (x >> 2) + (x & 1 && x & 2) * (n % ((__int128)1 << now) + 1);
x >>= 1, ++now;
}
printf("Case %d: ", ++t);
Print(ans), putchar('\n');
}
}