P1066 2^k进制数

P1066 2^k进制数

题目链接

题目描述

设 r 是个 2^k进制数,并满足以下条件:

  • r 至少是个 2 位的 2^k进制数。
  • 作为 2^k进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位。
  • 将 r转换为二进制数 q后,则 q的总位数不超过 w。

在这里,正整数 k,w 是事先给定的。

问:满足上述条件的不同的 r共有多少个?

我们再从另一角度作些解释:设 S是长度为 w 的 0101 字符串(即字符串 S 由 w 个 0 或 1 组成),S对应于上述条件三中的 q。将 S从右起划分为若干个长度为 k的段,每段对应一位 2^k 进制的数,如果 S至少可分成 2段,则 S所对应的二进制数又可以转换为上述的 2^k进制数 r。

例:设 k=3,w=7 k =3。则 r 是个八进制数( 2^3=8 )。由于 w=7,长度为 7 的 01 字符串按 33位一段分,可分为 3 段(即 1,3,3,左边第一段只有一个二进制位),则满足条件的八进制数有:

2 位数:
高位为 1:6 个(即 12,13,14,15,16,17 ),
高位为 2:5 个,
…,
高位为 6:1个(即 67 )。
共 6+5+…+1=21 个。

3位数:
高位只能是 1,
第 2位为 2:5 个(即 123,124,125,126,127),
第 2 位为 3:4 个,
…,
第 2位为 6:1个(即 167)。
共 5+4+…+1=15 个。

所以,满足要求的 r共有 36 个。

输入格式

一行两个正整数 k,w 用一个空格隔开:

输出格式

一行一个个正整数,为所求的计算结果。
即满足条件的不同的 r的个数(用十进制数表示),要求不得有前导零,各数字之间不得插入数字以外的其他字符(例如空格、换行符、逗号等)。

(提示:作为结果的正整数可能很大,但不会超过 200 位)

解析

首先我们来理解题意,r为k进制数,转化为二进制后,01串最多位不能超过w。由于k位01串可以组成一个k进制数,我们不妨将最多位可能的情况下的w按k位一组进行切分,得到L = ceil(w/k)组,那么r在k进制情况下最多有L位,最少有2位。

例如输入3,7时,r位8进制数,r的每位由3个二进制位构成,r最多情况下可能由7个二进制位构成。我们将7按3位一组进行切分,得到[1,3,3]。

r在k进制下最少只有两位,最多L位,则r的所有情况为 2位r下的情况到L位r下的情况总数之和。

以 3 7为例,则r可能有2位或3位,当r两位的时候,可能情况有 12,13,14,..17,23,24..27,67,即在1..7的数字中选择2个,构成二位的r。当r三位时,即首先选择出第一位,并在剩下的位中选择出2个比第一位大的数。

由此,可以看出这个问题实际上是组合数问题。

当前n个分段,每个分段均为k位时,则每段可选择的数字为2k-1,而r的可能情况为(2k-1)中取i位(i从2到n)

最后一个分段有可能不到k位,则首先从1开始选择,做出第一次选择后,计算出剩下n-1个分段的组合数,并依次求和,得到最终结果。

接下来问题就转换到如何计算组合数了。

计算组合数可以参考这篇博文 参考链接

此处使用杨辉三角递推求解组合数值。

最后本题中会出现大整数,超出 long long int范围,因此对于C++/C实现来说需要手写大整数

在此首先给出Java版本代码



import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Scanner;

public class Main {
    private static final int n = 1000;
    static BigInteger [][]comb = new BigInteger[1000][1000];
    static void init()
    {
        for(int i=0;i<n ;i++)
        {
            comb[i][0] = BigInteger.valueOf(1);
            comb[i][i] = BigInteger.valueOf(1);
            for(int j=1;j<i;j++)
            {
                comb[i][j] = comb[i-1][j].add(comb[i-1][j-1]);
            }
        }

    }


    static BigInteger C(int n, int m)
    {
        if(n<m) return BigInteger.valueOf(0);
        return comb[n][m];
    }

    public static void main(String[] args) {
        BigInteger cnt =BigInteger.valueOf(0);
        init();
        int k,w;
        Scanner in = new Scanner(System.in);
        k = in.nextInt();
        w = in.nextInt();
        int fullPart = w / k;
        int divPart = (int) Math.ceil(1.0*w / k);
        int resPart = 0;
        if (w % k != 0)
        {
            resPart = (int) (w - k * fullPart);
        }
        int sel = (int) (Math.pow(2, k) - 1);
        for (int i = 2; i <= divPart; i++)
        {
            if (i != divPart)
            {
                cnt = cnt.add(C(sel,i));
            }
            else
            {
                if (resPart != 0)
                {
                    int t = (int) (Math.pow(2, resPart) - 1);
                    for (int j = 1; j <= t; j++)
                    {
                        cnt = cnt.add(C(sel-j,i-1));
                    }
                }
                else
                {
                    cnt = cnt.add(C(sel,i));
                }
            }

        }
        System.out.println(cnt);
    }
}

C语言代码 此处大整数代码来自于其他博文,一时找不到出处

#include<stdio.h>
#include<string.h>
#include<math.h>
#define ll long long int
#define maxChar 130
#define maxlen 260
typedef struct HP
{int len;
char s[maxChar];
}HP;
void PrintHP(HP x) {for(int i=x.len;i>=1;i--) printf("%d",x.s[i]);}
void Str2HP(const char *s,HP *x)
{
	x->len=strlen(s);
	for(int i=1;i<=x->len;i++) x->s[i]=s[x->len-i]-'0'; //倒着记录
}
void Plus(const HP a,const HP b,HP*c)
{
	int i;c->s[1]=0;
	for(i=1;i<=a.len||i<=b.len||c->s[i];i++){
		if(i<=a.len) c->s[i]+=a.s[i];
		if(i<=b.len) c->s[i]+=b.s[i];
		c->s[i+1]=c->s[i]/10;c->s[i]%=10;
	}
	c->len=i-1;if(c->len==0) c->len=1;
}
HP comb[maxlen][maxlen];
void init()
{
        for(int i=0;i<maxlen ;i++)
        {
            
            Str2HP("1",&comb[i][0]);
            Str2HP("1",&comb[i][i]);
            for(int j=1;j<i;j++)
            {
                Plus(comb[i-1][j],comb[i-1][j-1],&comb[i][j]);
            }
        }
}
HP C(ll n, ll m)
{
    HP t;
    Str2HP("0",&t);
    if(n<m) return t;
    return comb[n][m];
}

int main()
{
    HP ans;
    HP temp;
    ll cnt = 0;
    Str2HP("0",&ans);
    ll k, w;
    init();
    scanf("%lld %lld", &k, &w);
    ll fullPart = w / k;
    ll divPart = ceil(1.0*w / k);
    int resPart = 0;
    if (w % k != 0)
        resPart = w - k * fullPart;
    int sel = pow(2, k) - 1;
    for (int i = 2; i <= divPart; i++)
    {
        if (i != divPart)
        {
            HP t=  C(sel,i);
            HP a = ans;
            Plus(a,t,&ans);
        }
        else
        {
            if (resPart != 0)
            {
                int t = pow(2, resPart) - 1;
                for (int j = 1; j <= t; j++)
                {
                    HP x = C(sel - j, (i - 1));
                    HP a = ans;
                    Plus(a,x,&ans);
                }
            }
            else
            {
                HP x = C(sel, i);
                HP a = ans;
                Plus(a,x,&ans);
            }

        }

    }
    PrintHP(ans);
}
posted @ 2020-11-20 17:47  alexhe101  阅读(112)  评论(0编辑  收藏  举报