思路
- 题意
- 给我们一个n( ), 让我们求从0~n中所有数在二进制下出现2个1相邻的总数量和 sum
- 分析
-
首先n很大,不可能暴力的求的,必须要找出0~n中连续2个1相邻出现的规律,
-
其次 总数量和sum 有可能超 long long 所以要注意用大数,
-
接下来我们说求解的方法:
-
我们假设 n的值在二进制下为:
1101101110
,我们从二进制的高位向地低位(从二进制左->向右)遍历求解所有可能产生答案的数字,当我们讨论最左边的第一位时候,我们把这一位当做0,那么现在我们可以把这个二进制数看为:0 _ _ _ _ _ _ _ _ _
, -
现在我们要考虑的是:0后面的9位
_ _ _ _ _ _ _ _ _
,产生连续11
规律, -
我们可以在0后面的9位上:填上连续的数字如:
- 000000000
- 000000001
- 000000010
- 000000011
- 000000100
- …
- …
- 111111111
-
总共可以填写连续且不同的01数字串的数量为: ,
-
我们可以用概率方法求出练习的11子串产生规律,我们我发现 对于二进制中从的每一位出现1的概率是1/2, 那么连续两个11出现的概率就是 1/4. 举个例子证明一下:例如从 000~111
- 000
- 001
- 010
- 011
- 100
- 101
- 110
- 111
-
我们观察从 0~7 这个8个数,
- 二进制下左边第1位1出现了4次,0出现了4次,
- 二进制下左边第2位1出现了4次,0出现了4次
- 二进制下左边第3位1出现了4次,0出现了4次
- 且连续11出现次数为:4次 =
-
这样我们就可以证明出:连续11出现的概率位1/4,那么我们回到:0后面的9位数
_ _ _ _ _ _ _ _ _
总共 个子串产生的连续11次数为: , -
还有一种情况我们要注意:假如我们遍历讨论到: 的左边第二位,这个时候由于左边第二位为1,而且这一位的左边那一个二进制为也是1,这个时候,已经出现了连续两个11的情况:
11 _ _ _ _ _ _ _ _
,这个时候我们的答案要加上:除去最左边1、2位之后剩下的数字(这个剩下的数字就表示方案数):01101110
, -
之后就是一位一位的遍历考虑了。。。。。。
-
-
注意:我给了两种方法的代码,但是这两种方法的核心思想都一样,只不过一个从 二进制的左边向右边开始遍历 统计方案,代码二是从 二进制的 右边向左边遍历 统计方案数
-
在对于大数的使用方面:代码一 使用结构体,来操作大数,
要注意一点就是存大数的数组中的每个元素存储大数的4位
,而代码二:的大数则是通过两个long long 变量a、b 存储大数,b存储大数的前面的前13位,a则存储剩下的大数的位数,这种存储方法,适用于 大数位数不超过31位(在十进制下)
代码一
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
#define pr printf
using namespace std;
const int N = 10;
const int K = 10000;
char format[] = "%04d";
struct Bignum
{
ll a[N * 2];
Bignum() { memset(a, 0, sizeof(a)); }
Bignum(ll num)
{
memset(a, 0, sizeof(a));
int i = 0;
while(num) //注意 a 中的每个元素保留 大数中的4位
a[i ++] = num % K, num /= K;
}
void maintain()
{
for(int i = 0; i < N; i ++)
if(a[i] > K)
a[i + 1] += a[i] / K, a[i] %= K;
}
Bignum operator + (const Bignum & b) //Bignum类型 + Bignum类型
{
Bignum c;
for(int i = 0; i < N; i ++)
c.a[i] = a[i] + b.a[i];
c.maintain();
return c;
}
Bignum operator + (ll num) //Bignum类型 + 常数
{
return * this + Bignum(num);
}
Bignum operator * (const Bignum & b)
{
Bignum c;
for(int i = 0; i < N; i ++)
for(int j = 0; j < N; j ++)
c.a[i + j] += a[i] * b.a[j];
c.maintain();
return c;
}
void print()
{
int digit = 2 * N;
while(a[-- digit] == 0 && digit > 0);
printf("%lld", a[digit --]);
for(int i = digit; i >= 0; i --)
printf(format, abs(a[i]));
printf("\n");
}
};
ll d[1005], cd = 0; //d 存储 题目所给的数字的二进制形式,cd 为 转化之后的二进制数的 位数
ll two[65];
void init()
{
two[0] = 1;
for(int i = 1; i <= 64; i ++)
two[i] = two[i - 1] * 2;
}
void convert_binary(ll n)
{
cd = 0;
memset(d, 0, sizeof(d));
while(n)
d[cd ++] = n % 2, n >>= 1;
}
int cas = 1;
void work(ll n)
{
convert_binary(n);
Bignum ans(0);
for(int i = cd - 1; i >= 0; i --)
{
if(d[i] == 1)
{
if(i >= 2)
ans = ans + Bignum(two[i - 2]) * (i - 1);
if(d[i + 1] == 1)
ans = ans + (n % two[i] + 1);
}
}
printf("Case %d: ", cas ++);
ans.print();
}
int main()
{
/* fre(); */
init();
ll n;
while(scanf("%lld", &n) && n >= 0)
work(n);
return 0;
}
代码二
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
#define pr printf
using namespace std;
const ll MOD = 1e13;
ll a, b;
void add(ll x)
{
b += x;
a += b / MOD;
b %= MOD;
}
void solve(ll n)
{
ll digit = 1, v = n;
a = b = 0;
while(n)
{
add((n >> 2) * digit);
if((n & 3) == 3)
add((v & (digit - 1)) + 1);
n >>= 1;
digit <<= 1;
}
if(a)
printf("%lld%013lld\n", a, b);
else
printf("%lld\n", b);
}
int main()
{
/* fre(); */
ll n, cas = 1;
while(scanf("%lld", &n) && n >= 0)
{
printf("Case %lld: ", cas ++);
solve(n);
}
return 0;
}