noip2006 2^k进制数
描述
设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. 实际编程时存在一些计算边界。
代码如下:
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 }