您的位置:首页 > 其它

ural1057 Amount of Degrees数位统计入门题

2015-08-12 21:30 459 查看
        题目大意:给你一个区间,由[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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数位统计 dp