P1066 2^k进制数
高精+组合数学
读题可得,满足条件的2^k进制数可分为2种
1.位数为2~w/k的
2.位数为w/k+1(w%k!=0)的
对于第一种,就是求2^k-1个数中不重复取 i 个的组合数,就是C(2^k-1,2)+...+C(2^k-1,w/k).
对于第二种,由于题目限制每一位严格小于右边相邻的一位,所以取数时只能越来越小,即C(2^k-1-i,w/k),其中 i 为最高位的所有可取值1~2^(w%k)-1
高精需要压位,不然会MLEqwq
#include<cstdio> #include<cstring> using namespace std; const int wid=10000; //压4位 struct data{ //个人觉得重载好写qwq 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; u.len=len+tmp.len-1; 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>=1;--i){ for(int j=10;a[i]*j<wid;j*=10) printf("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; //注意防止上限溢出2^9=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]+c[i-1][j-1]; //预处理组合数 for(int i=2;i<=p;++i) ans=ans+c[n][i]; //第一类 for(int i=(1<<w%k)-1;i>=1;--i) ans=ans+c[n-i][p];//第二类 ans.print(); return 0; }