ural1057 Amount of Degrees数位统计入门题

来源:互联网 发布:vb6 连接mysql数据库 编辑:程序博客网 时间:2024/05/29 03:23

        题目大意:给你一个区间,由[m,n]表示,然后给你一个k,一个b,分别代表在这个区间内的数字的b进制数内含有k个1的数字有几个。

        很明显,此题满足区间减法,就是[m,n]的值就等于[0,m]的值减去[0,n-1]的值,注意是n-1,我因为这个WA了一发,还找了半个小时的错,很奇怪,这个写错了之后数据也不好找,提供一组数据:13974 28320 8 2,answer应该是3148

        在知道有数位统计这个东西之前,我是想用组合数学做的,大体思想就是统计出大数的最高位与小数的最高位之间有几位,然后填坑法求解,只是有个想法,并没有把代码写出来。后来上了数位统计的集训课,就试着用这道题联系一下,其实思路很简单,用一个数组f[i][j]来记录前i位二进制数中含有j个1的有几个,这里有一个递推公式:f[i][j] = f[i-1][j] + f[i-1][j-1],原因很简单,对于第i位,要么为1,要么为0,如果是1,就是f[i-1][j-1],如果是0,就是f[i-1][j]。初始设f[0][0]为1,dp出来就好了

        本题解法,就是计算出来从0到n的二进制数含有k个1的有几个,注意最后要讨论这个数本身是不是,此处用到的算法:x&(1<<i),判断x第i位是不是1;x^(1<<i),让x等于x去掉最高位的1的数。

        最后对于非二进制,只要找到b进制第一个大于1的位置,然后从这个位置往后全部置为1就好了,因为题目要求就是找有几个1,大于1的话就是说明这个数比后面全是1的数大,就可以直接用后面全是1的数代替。

AC代码:

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <cstdio>#include <cstring>#include <algorithm>#include <iostream>using namespace std;int f[35][35];int a[35];void init(){    memset(f,0,sizeof(f));    f[0][0] = 1;    for(int i = 1; i <= 31; i++)    {        f[i][0] = f[i-1][0];        for(int j = 1; j <= i; j++)        {            f[i][j] = f[i-1][j] + f[i-1][j-1];        }    }}int count(int x, int k){    int tot = 0;    int ans = 0;    for(int i = 31; i > 0; i--)    {        if(x&(1<<i))        {            tot++;            if(tot > k) break;            x = x^(1<<i);        }        if((1 << (i - 1)) <= x)        ans+=f[i-1][k-tot];    }    if(tot + x == k) ++ans;   //此时x == 0,答案加上x    return ans;}int main(){    //freopen("in.txt","r",stdin);    init();    int m,n,k,b;    while(scanf("%d%d%d%d",&m,&n,&k,&b)!=EOF)    {        //cout<<count(13974,8)<<endl<<count(28320, 8);        if(b == 2)                                 //此处写麻烦了,可以整个都用数组存起来的,不用再转化成数字,这样的话函数内部也得改        printf("%d\n",count(n,k) - count(m-1,k));        else        {            int mm,nn;            mm = nn =0;            int tem = 0;            while(m){                a[tem++] = m % b;                m /= b;            }            int cn = 1;            for(int i = tem - 1; i >= 0; i--)            {                if(a[i] > 1)                {                    for(int j = i; j >= 0; j--)                    a[j] = 1;                    break;                }            }            for(int i = 0; i < tem; i++)            {                mm+=a[i]*cn;                cn*=2;            }            tem = 0;            while(n)            {                a[tem++] = n %b;                n /= b;            }            cn = 1;            for(int i = tem - 1; i >= 0; i--)            {                if(a[i] > 1)                {                    for(int j = i; j >= 0; j--)                    a[j] = 1;                    break;                }            }            for(int i = 0; i < tem; i++)            {                nn+=a[i]*cn;                cn*=2;            }            printf("%d\n",count(nn,k) - count(mm-1,k));        }    }}</span>

0 0