P1066 2^k进制数
好好看题
看懂了就不难了
在2^k进制下,一位一位看
每一位都有一些数可以填
除非是最左边一位,不然可以填的数最大都是 2^k-1
所以显然当填的位数为 i 时(不是最后一位),可能的选取方案总共有 C(2^k-1,i )
如果要填最后一位
最后一位可以填的最大的数为 2^ (w%k)-1
那就枚举一下最后的数,前面选取的数都要大于它
那么当最后的数为 i 时,前面的数选取方案数为 C( (2^k-1) -i,w/k)
(注意 w/k 可能很大,但是如果超过 2^9-1 那就没意义了(能够选的数最大只有 2^9-1))
因为答案很大,要用高精度
我用的是压位高精(借着这一题学了一下压位高精...)
(感谢crk大佬为我提供压位高精的代码 %%%)
最后要注意一下细节
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int wid=10000;//压位高精,每4位放一起 struct data { int a[52],len; data(){ memset(a,0,sizeof(a)); len=0; } //初始化 data operator + (data &tmp)//重载加号 { data u; int x=0; u.len= len>tmp.len ? len : tmp.len; for(int i=1;i<=u.len;i++) { u.a[i]=a[i]+tmp.a[i]+x; x=u.a[i]/wid; u.a[i]%=wid; } while(x) u.a[++u.len]=x%wid,x/=wid; return u; } data operator * (data &tmp) //重载乘号,其实用不上,顺便一起打了 { data u; int x=0; for(int i=1;i<=tmp.len;i++,x=0) { for(int j=1;j<=len;j++) { u.a[i+j-1]+=tmp.a[i]*a[j]+x; x=u.a[i+j-1]/wid; u.a[i+j-1]%=wid; }//注意细节 u.a[i+len]+=x; } while(u.a[u.len+1]) u.a[u.len+2]+=u.a[u.len+1]/wid,u.a[(++u.len)+1]%=wid; //一堆细节.. return u; } void print() { printf("%d",a[len]); for(int i=len-1;i;i--) { for(int j=10;j*a[i]<wid;j*=10) printf("0"); //如果压的数还不到4位就要补上0 printf("%d",a[i]); } } }C[513][513],ans; int k,w,n,p; int main() { scanf("%d%d",&k,&w); n=(1<<k)-1; p=w/k>512 ? 512 : w/k;//p最大为512 for(int i=0;i<=n;i++) C[i][0].a[1]=1,C[i][0].len=1; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j]; //求组合数 for(int i=2;i<=p;i++) ans=ans+C[n][i]; for(int i=(1<<w%k)-1;i;i--) ans=ans+C[n-i][p];//i不能取0,细节 ans.print(); return 0; }