1.对于一个数 n,假设它二进制下有 m 个 1,分别是第a_1,a_2…a_m位。对于 n 的任意一种分解方案,把所有 2 的幂升序排序,然后可以划分成 m 段,其中第 i 段的和是 $2^{a_i} 2.对于一个数$2^i的一种划分方案,如果不是只有它本身一个数,一定可以把这些 2 的幂升序排序,然后分成两段,每一段的和都是 $2^{i−1}$
证明并不难。有了这些性质,就可以设新的状态了。
首先设 g[i][j]表示做完了前 i 段(即 n 二进制下的前 i 个 1 位),最大的数是 $2^j,方案数是多少。 再设一个辅助数组f[i][j],表示组成$2^i,最大的数是 $2^j的方案数。 接下来枚举第i-1段的最大数是多少,假设是$2^k,那么g[i][j]=\sum^{j}_{k=0}g[i−1][k]∗f[i−k][j−k] 其中i-k,j-k的意义在于:要控制后面的数都大于等于 $2^k,每个数都要除掉$2^k,那么就不会算重了。 f[i][j]的转移类似
# include <cstdio> # include <cstring> # include <algorithm>
using namespace std;
const int N=99,M=170,mo=1e9;
typedef long long LL;
char c[N];
int tot;
struct num { int w,a[M]; }f[N+5][N+5],ans,n,p,t,s[N+5],g[N+5][N+5];
num operator + (const num &a,const num &b) { t.w=max(a.w,b.w); //t.a[1]=0; memset(t.a,0,sizeof(t.a)); for (int i=1;i<=t.w;i++) { t.a[i]+=a.a[i]+b.a[i]; if (t.a[i]>=mo) { t.a[i+1]=1; t.a[i]-=mo; }//else t.a[i+1]=0; } if (t.a[t.w+1]>0) t.w++; //memset(t.a+t.w+1,0,sizeof(t.a+t.w+1)); return t; }
num operator * (const num &a,const num &b) { memset(t.a,0,sizeof(t.a)); for (int i=1;i<=a.w;i++) { for (int j=1;j<=b.w;j++) { t.a[i+j]+=(t.a[i+j-1]+(LL)a.a[i]*b.a[j])/mo; t.a[i+j-1]=(t.a[i+j-1]+(LL)a.a[i]*b.a[j])%mo; } } for (t.w=a.w+b.w;t.w>1 && t.a[t.w]==0;t.w--); return t; }
int main() { freopen("data.out","w",stdout); scanf("%s",c+1); int l=strlen(c+1); n.w=l/9+1; for (int i=1;i<=l;i++) { int j=(l-i)/9; n.a[j+1]=n.a[j+1]*10+c[i]-48; } f[0][0].w=f[0][0].a[1]=1; for (int i=1;i<=N;i++) { for (int j=0;j<i;j++) { for (int k=0;k<=j;k++) { f[i][j]=f[i][j]+f[i-1][k]*f[i-1-k][j-k]; } } f[i][i].w=f[i][i].a[1]=1; } tot=0; for (int i=0;i<=N;i++) { if (n.a[1]&1) { tot++; if (tot==1) { for (int j=0;j<=i;j++) g[1][j]=f[i][j]; }else { for (int j=0;j<=i;j++) { for (int k=0;k<=j;k++) { g[tot][j]=g[tot][j]+g[tot-1][k]*f[i-k][j-k]; } } } } int r=0; for (int j=n.w;j;j--) { int t=n.a[j]; n.a[j]=((LL)r*mo+t)/2; r=((LL)r*mo+t)%2; } } for (int i=0;i<=N;i++) ans=ans+g[tot][i]; printf("%d",ans.a[ans.w]); for (int i=ans.w-1;i;i--) { for (int j=1e8;j;j/=10) { printf("%d",ans.a[i]/j); ans.a[i]%=j; } } printf("\n"); return 0; }