URAL 1057 Amount of Degrees

数位DP,题意转换一下就是说,其B进制表示的各位数字都只能是0和1。

求区间L....R之间的符合要求数字有几个,可以使用容斥原理。

计算小于X的符合要求的数有几个的时候,我们需要先找到比X小的最大的符合要求的数字,然后以这个数字往下推导,和之前做的数位DP方法一样了。

dp[i][j][k]表示的是i位,首位数字为j,有k个1的数的个数是多少。

递推式很容易写出来:

dp[i][0][k] += dp[i - 1][0][k];
dp[i][0][k] += dp[i - 1][1][k];

dp[i][1][k] += dp[i - 1][0][k - 1];
dp[i][1][k] += dp[i - 1][1][k - 1];

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

int dp[35][5][30];
int B,K;
int L,R;
int p[35];
int tot;

void init()
{
    memset(dp,0,sizeof dp);

    dp[1][0][0]=1;
    dp[1][1][1]=1;

    for(int i=2; i<=32; i++)
    {
        for(int j=0; j<=1; j++)
        {
            for(int k=0; k<=20; k++)
            {
                if(j==0)
                {
                    dp[i][0][k]+=dp[i-1][0][k];
                    dp[i][0][k]+=dp[i-1][1][k];
                }
                else if(j==1)
                {
                    if(k-1>=0)
                    {
                        dp[i][1][k]+=dp[i-1][0][k-1];
                        dp[i][1][k]+=dp[i-1][1][k-1];
                    }
                }
            }
        }
    }
}

int f(int x)
{
    tot=1;
    while(x)
    {
        p[tot++]=x%B;
        x=x/B;
    }
    tot--;

    //找到第一个比x小,并且能表示的数字
    for(int i=tot; i>=1; i--)
    {
        if(p[i]!=1&&p[i]!=0)
        {
            for(int j=i; j>=1; j--) p[j]=1;
            break;
        }
    }


    int res=0;
    for(int i=1; i<tot; i++) res=res+dp[i][1][K];

    int sum=1;//高位确定有sum个1
    for(int i=tot-1; i>=1; i--)
    {
        if(p[i]==1)
        {
            res=res+dp[i][0][K-sum];
            sum++;
        }
    }
    sum=0;
    for(int i=1; i<=tot; i++)
        if(p[i]==1) sum++;
    if(sum==K) res++;
    return res;
}

int main()
{
    init();
    while(~scanf("%d%d",&L,&R))
    {
        scanf("%d%d",&K,&B);
        printf("%d
",f(R)-f(L-1));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zufezzt/p/5168332.html