题解 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');
	}
}
posted @ 2020-02-17 20:13  whx1003  阅读(178)  评论(0编辑  收藏  举报