A - Bits UVA - 11645(位运算 + 大数)

思路

  • 题意
  1. 给我们一个n( n < = 2 63 2 n<=2^{63}-2 ), 让我们求从0~n中所有数在二进制下出现2个1相邻的总数量和 sum
  • 分析
  1. 首先n很大,不可能暴力的求的,必须要找出0~n中连续2个1相邻出现的规律,

  2. 其次 总数量和sum 有可能超 long long 所以要注意用大数,

  3. 接下来我们说求解的方法:

    1. 我们假设 n的值在二进制下为:1101101110,我们从二进制的高位向地低位(从二进制左->向右)遍历求解所有可能产生答案的数字,当我们讨论最左边的第一位时候,我们把这一位当做0,那么现在我们可以把这个二进制数看为:0 _ _ _ _ _ _ _ _ _

    2. 现在我们要考虑的是:0后面的9位_ _ _ _ _ _ _ _ _,产生连续11规律,

    3. 我们可以在0后面的9位上:填上连续的数字如:

      1. 000000000
      2. 000000001
      3. 000000010
      4. 000000011
      5. 000000100
      6. 111111111
    4. 总共可以填写连续且不同的01数字串的数量为: 2 10 2^{10} ,

    5. 我们可以用概率方法求出练习的11子串产生规律,我们我发现 对于二进制中从的每一位出现1的概率是1/2, 那么连续两个11出现的概率就是 1/4. 举个例子证明一下:例如从 000~111

      1. 000
      2. 001
      3. 010
      4. 011
      5. 100
      6. 101
      7. 110
      8. 111
    6. 我们观察从 0~7 这个8个数,

      1. 二进制下左边第1位1出现了4次,0出现了4次,
      2. 二进制下左边第2位1出现了4次,0出现了4次
      3. 二进制下左边第3位1出现了4次,0出现了4次
      4. 且连续11出现次数为:4次 = 2 4 ( 1 / 4 ) 2^4*(1/4)
    7. 这样我们就可以证明出:连续11出现的概率位1/4,那么我们回到:0后面的9位数_ _ _ _ _ _ _ _ _总共 2 10 2^{10} 个子串产生的连续11次数为: 2 10 ( 1 / 4 ) 2^{10}*(1/4) ,

    8. 还有一种情况我们要注意:假如我们遍历讨论到: 1101101110 `1101101110` 的左边第二位,这个时候由于左边第二位为1,而且这一位的左边那一个二进制为也是1,这个时候,已经出现了连续两个11的情况:11 _ _ _ _ _ _ _ _,这个时候我们的答案要加上:除去最左边1、2位之后剩下的数字(这个剩下的数字就表示方案数):01101110,

    9. 之后就是一位一位的遍历考虑了。。。。。。

  4. 注意:我给了两种方法的代码,但是这两种方法的核心思想都一样,只不过一个从 二进制的左边向右边开始遍历 统计方案,代码二是从 二进制的 右边向左边遍历 统计方案数

  5. 在对于大数的使用方面:代码一 使用结构体,来操作大数,要注意一点就是存大数的数组中的每个元素存储大数的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;
}

猜你喜欢

转载自blog.csdn.net/qq_34261446/article/details/107452705