noip2006 2^k进制数

时间:2022-12-16 13:41:07

描述

设r是个2k 进制数,并满足以下条件:
(1)r至少是个2位的2k 进制数。

(2)作为2k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位。

(3)将r转换为2进制数q后,则q的总位数不超过w。

在这里,正整数k(1≤k≤9)和w(k<W≤30000)是事先给定的。

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

例:设k=3,w=7。则r是个八进制数(23=8)。由于w=7,长度为7的01字符串按3位一段分,可分为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个。

格式

输入格式

输入文件只有1行,为两个正整数,用一个空格隔开:
k W

输出格式

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

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

-----------------------------------------------------------------------
正解=组合数学
注意这句话:作为2^k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位。
其实这是在暗示组合数,
      显然r中的不会有相同的位
      如果每一位都不同,显然只有严格递增的排列是合法的
这便是组合- =,
将 r 转化成这种形式(设k为3) 000 000 000 000 
      显然除首位外每一位的取值范围为 000 to 111(2^k-1)
      在首位为0的情况下,最多可取 w/k 位,且题目要求大于2位,
      则在首位为0的合法解有  C(2^k-1,i)(2<=i<=w/k) ,
Ps. 如果 w 模 k 等于 0 仅考虑上述情况即可。
考虑首位不为0的情况
      显然首位不为0的话,r 就有 w/k+1 位,
      除首位外还有w/k位,
      可以枚举首位的取值范围为 1 to 2^(w mod k)-1
      设首位取值为 val  
      则剩下 w/k 位 取值范围为 val+1 to 2^k-1 
      也就是有 2^k-1-val 个数可取
      所以首位不为0的合法解有    C(2^k-1-val,w/k)(1<=val<=2^(w mod k)-1) 
所以上述两者相加便是正解(需要高精运算)
Ps. 实际编程时存在一些计算边界。
代码如下:
noip2006 2^k进制数noip2006 2^k进制数
 1 #include<cstring>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<string>
 5 #include<iostream>
 6 #include<queue>
 7 #define INF 99999999
 8 #define LL long long
 9 using namespace std;
10 struct STR{
11     int s[200];
12 }c[501][501]; 
13 STR One,ans;
14 void put(STR a){
15     printf("%d",a.s[a.s[0]]);
16     for(int i=a.s[0]-1;i>0;i--) printf("%.4d",a.s[i]);
17 }
18 STR add(STR &d,STR a,STR b){
19     int L=max(a.s[0],b.s[0]);
20     for(int i=1;i<=L;i++){
21         d.s[i]+=a.s[i]+b.s[i];
22         d.s[i+1]+=d.s[i]/10000;
23         d.s[i]%=10000;
24     }
25     if(d.s[L+1]) ++L;
26     d.s[0]=L;
27     return d;
28 }
29 void Inc(STR &a,STR b){
30     int L=max(a.s[0],b.s[0]);
31     for(int i=1;i<=L;i++){
32         a.s[i]+=b.s[i];
33         a.s[i+1]+=a.s[i]/10000;
34         a.s[i]%=10000;
35     }
36     if(a.s[L+1]) ++L;
37     a.s[0]=L;
38 }
39 STR C(int n,int m){
40     if(c[n][m].s[0]) return c[n][m];
41     if(n==m||!m) return c[n][m]=One;
42     return add(c[n][m],C(n-1,m-1),C(n-1,m));
43 }
44 int main(){
45     One.s[0]=One.s[1]=1;
46     int k,w;
47     scanf("%d%d",&k,&w);
48         int Max=w/k,lim1=1<<k;
49         --lim1;
50         for(int i=2;i<=Max;i++) 
51       if(i<=lim1)
52         Inc(ans,C(lim1,i));
53     
54          int lim2=1<<(w%k);
55     for(int j=1;j<lim2&&lim1-j>=Max;j++) 
56        Inc(ans,C(lim1-j,Max));
57     put(ans);
58 }
View Code