2251: 2^k进制数(NOIP 2006 TGT4)

题目描述

设 r� 是个 2k2� 进制数,并满足以下条件:
- r� 至少是个 22 位的 2k2� 进制数。
- 作为 2k2� 进制数,除最后一位外,r� 的每一位严格小于它右边相邻的那一位。
- 将 r� 转换为二进制数 q� 后,则 q� 的总位数不超过 w�。
在这里,正整数 k,w�,� 是事先给定的。
问:满足上述条件的不同的 r� 共有多少个?
我们再从另一角度作些解释:设 S� 是长度为 w� 的 0101 字符串(即字符串 S� 由 w� 个 00 或 11 组成),S� 对应于上述条件三中的 q�。将 S� 从右起划分为若干个长度为 k� 的段,每段对应一位 2k2� 进制的数,如果 S� 至少可分成 22 段,则 S� 所对应的二进制数又可以转换为上述的 2k2� 进制数 r�。
例:设 k=3,w=7�=3,�=7。则 r� 是个八进制数( 23=823=8 )。由于 w=7�=7,长度为 77 的 0101 字符串按 33 位一段分,可分为 33 段(即 1,3,31,3,3,左边第一段只有一个二进制位),则满足条件的八进制数有:
22 位数:
高位为 11:66 个(即 12,13,14,15,16,1712,13,14,15,16,17 ),
高位为 22:55 个,
…,
高位为 66:11 个(即 6767 )。
共 6+5+…+1=216+5+…+1=21 个。

33 位数:
高位只能是 11,
第 22 位为 22:55 个(即 123,124,125,126,127123,124,125,126,127 ),
第 22 位为 33:44 个,
…,
第 22 位为 66:11 个(即 167167 )。
共 5+4+…+1=155+4+…+1=15 个。
所以,满足要求的 r� 共有 3636 个。

输入

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

输出

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

样例输入 

3 7

样例输出 

36

提示

【数据范围】
1≤k≤91≤�≤9
1≤w≤3×104

#include<cstdio>
#include<iomanip>
#include<iostream>
#include<cstring>
using namespace std;
const int N=205;
const int M=1<<9;
string C[M][M],p,t,ans;
int a[N],b[N],c[N];
int w,k;
inline int Swap(int &x,int &y){int t=x;x=y;y=t;}
  
string Add(string sa,string sb)//高精度加法 
{
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(b,0,sizeof(c));
    t="";
    int lena=sa.size();
    int lenb=sb.size(),lenc;
    for(int i=0;i<lena;i++) a[i+1]=sa[lena-i-1]-'0';
    for(int i=0;i<lenb;i++) b[i+1]=sb[lenb-i-1]-'0';
    int i=1,x=0;
    while(i<=lena||i<=lenb){
        c[i]=a[i]+b[i]+x;
        x=c[i]/10;
        c[i]%=10;
        i++;
    }
     
    if(x) c[i]=x;
    else i--;
    lenc=i;
    for(int i=lenc;i>=1;i--) t+=(char)c[i]+'0';
    return t;
}
int main()
{
    scanf("%d%d",&k,&w);
    int ed=1<<k,sp=1<<w%k;
     
    C[0][0]="1";
    for(int i=1;i<=ed;i++) C[i][0]=C[i][i]="1";
    for(int i=2;i<=ed;i++){
        for(int j=1;j<i;j++) C[i][j]=Add(C[i-1][j],C[i-1][j-1]);//预处理组合数 
    }
     
    //这里其实可以将k整除w和k不整除w两种情况写在一起 
    for(int i=2;i<=w/k;i++){
        if(i>ed-1) break;
        ans=Add(ans,C[ed-1][i]);//累加 
    }
    for(int i=1;i<=sp-1;i++){
        if(w/k>ed-i-1) break;
        ans=Add(ans,C[ed-i-1][w/k]);//累加 
    }
    int len=ans.size();
    for(int i=0;i<len;i++) printf("%c",ans[i]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值