代码改变世界

[xdoj] 1301&1302 数字计数 数字计数的复仇

2018-05-27 01:04  legend聪  阅读(466)  评论(0编辑  收藏  举报

1.首先需要掌握二进制数的一种特性,00=0,01=1,10=2,11=3.每一个二进制的值代表他前面的二进数的个数,比如11=3,他的前面就有三个二进制的数字,不过在本题中,题目数据是1-n,故把0抛弃掉,答案就变成了3-1=2个,但还要加上自身的话,就还是二进制的值。比如10=2,前面有01和10.11=3,前面有01 10 11.以此类推。
2.那么问题就转化成了要求出小于等于给出十进制数字的最大二进制数,求出他的值即可。
比如123,最大就是111,结果就是7.
3. 问题转化成求最大的二进制数,如果十进制数的某一位大于1,那么他后面全可以取1能满足最大,对于右侧方法用po函数来计算这些全是1的位构成的二进制数字的最大值,对于左侧来说,把右侧的结果ans+1就变成了前一位2的n次方对应的值,相加即可得到最后的最大二进制数字。
4.关于数据范围:这是最令人讨厌的。
第一点假如想用pow来算那些位的种类数想都不要想,没有每次的取模,2的1000000次方是一个非常大的数字,对于10位当然是可以的最大才1024,但1000000分分钟超pow的范围。
第二点因为结果要对e9取模,可想而知每次运算的ans是e9这个数量级的范围,而那个每次的a*a更是一个超级大的数字,每次取模也是一个e9范围的数字,e9乘以一个e9就是e18,Long long的最大值是超过1e18的,只要单次不超范围,以后就可以取余。假如选择其他的这个乘法会导致溢出,所以肯定不行。1e9+7是一个经常出现的取模数字,它可以保证相加不爆int,相乘不爆longlong。这是应该记住的常识。
5.本题输入可是一个1000000位的数字,什么类型都没办法装下的,所以用字符串。而且是string,可以自动拓展,可以不用考虑开数组的大小的问题。
6.本题最难的点当然是计算了,用到了以前接触很少的左右移操作,及时进行取模。

#include <bits/stdc++.h>
const int Mod = 1e9 + 7;
using namespace std;
typedef long long ll;
ll po(ll a, ll b)
{
    ll ans=1;
    while (b)
    {
        if (b & 1)
        {
            ans = ans*a%Mod;
        }
        b >>= 1;
        a = a*a%Mod;
    }
    return ans;
}
int main()
{
    string str;
    ll ans;
    int i;
    while (cin >> str)
    {
        for (i = 0; i < str.length(); i++)
        {
            if (str[i] > '1')
            {
                break;
            }
        }
        ll l = str.length();
        ans = po(2, l - i) - 1;
        ll n;
        n = ans + 1;
        i--;
        for (; i >= 0; i--)
        {
            if (str[i] == '1')
                ans += n * 1;
            ans=ans%Mod;
            n = n * 2;
            n=n%Mod;
        }
        printf("%lld\n", ans);
    }
    
}