题目
任何正整数都能分解成2的幂,给定整数N,求N的此类划分方法的数量。
V1:
n≤106
n
≤
10
6
。要对答案模
1e9+7
1
e
9
+
7
。
V2:
n≤1030
n
≤
10
30
。将整个答案输出。
解题思路
考虑每一种划分方法的序列是怎样从一个空的序列变来的。
显然,它是要么
+1
+
1
,要么集体乘
2
2
。
因此很快乐地得出
当然,如果要与
V2
V
2
接轨,那么要换一种思路。
考虑将
n
n
转换为二进制,则。
因此,如果知道每个
2i
2
i
划分为2的幂的和的方案数,那么这题就很好做了。
对于某个
2i
2
i
的一种划分方案,如果不是只有它本身一个数,一定可以把这些2的幂按照升序排序,然后分成两段,每一段的和都是
2i−1
2
i
−
1
。
设
g[i][j]
g
[
i
]
[
j
]
表示做完了
2
2
进制下的前位,最大的数为
2j
2
j
的方案数。
设
f[i][j]
f
[
i
]
[
j
]
表示组成
2i
2
i
,最大的数为
2j
2
j
的方案数。
有人想到这样的转移方程:
g[i][j]=Σjk=0 g[i−1][k]∗f[i][j]
g
[
i
]
[
j
]
=
Σ
k
=
0
j
g
[
i
−
1
]
[
k
]
∗
f
[
i
]
[
j
]
f[i][j]=Σjk=0 f[i−1][k]∗f[i−1][j]
f
[
i
]
[
j
]
=
Σ
k
=
0
j
f
[
i
−
1
]
[
k
]
∗
f
[
i
−
1
]
[
j
]
但是这样会算重。
为了避免这,所以强制让后面的数字大于等于
2k
2
k
。
所以将后面的数字都除以
2k
2
k
之后,可以得出这么一个方程:
g[i][j]=Σjk=0 g[i−1][k]∗f[i−k][j−k]
g
[
i
]
[
j
]
=
Σ
k
=
0
j
g
[
i
−
1
]
[
k
]
∗
f
[
i
−
k
]
[
j
−
k
]
f[i][j]=Σjk=0 f[i−1][k]∗f[i−1−k][j−k]
f
[
i
]
[
j
]
=
Σ
k
=
0
j
f
[
i
−
1
]
[
k
]
∗
f
[
i
−
1
−
k
]
[
j
−
k
]
显然先预处理
f
f
。
然后套上高精度,再卡常,就可以通过此题了。
卡常很重要,就是拿个,确定一下
log2(n)
l
o
g
2
(
n
)
然而不知道为什么,高精度压8位WA了,压9位就没事?!
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100
#define M 150
#define mo 1000000000
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
int w,a[M];
};note f[N][N],g[N][N],ans,n,c;
int i,j,k,l,ys,x,tot;
int xx;
char s[32];
double x1,x2,x3;
note operator + (const note &a,const note &b){
c.w=a.w>b.w?a.w:b.w;
memset(c.a,0,sizeof(c.a));
int i;
fo(i,1,c.w){
c.a[i]+=a.a[i]+b.a[i];
c.a[i+1]+=c.a[i]/mo;
c.a[i]%=mo;
}
if(c.a[c.w+1])c.w++;
return c;
}
note operator * (const note &a,const note &b){
memset(c.a,0,sizeof(c.a));
int i,j;
long long kk;
fo(i,1,a.w)fo(j,1,b.w){
kk=c.a[i+j-1]+1ll*a.a[i]*b.a[j];
c.a[i+j]=c.a[i+j]+kk/mo;
c.a[i+j-1]=kk%mo;
}
c.w=a.w+b.w;
while(!c.a[c.w]&&c.w>1)c.w--;
return c;
}
int main(){
scanf("%s",s+1);
l=strlen(s+1);
fo(i,1,l){
x1=x1*10+s[i]-'0';
}
x2=log(x1)/log(2);
n.w=l/9+1;
fo(i,1,l){
int j=(l-i)/9;
n.a[j+1]=n.a[j+1]*10+(s[i]-'0');
}
while(!n.a[n.w]&&n.w>1)n.w--;
f[0][0].w=f[0][0].a[1]=1;
fo(i,1,x2){
fo(j,0,i-1)fo(k,0,j)
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;
}
fo(i,0,x2){
if(n.a[1]&1){
tot++;
if(tot==1){
fo(j,0,i)g[tot][j]=f[i][j];
}else{
fo(j,0,i)fo(k,0,j)
g[tot][j]=g[tot][j]+g[tot-1][k]*f[i-k][j-k];
}
}
ys=0;
fd(j,n.w,1){
x=n.a[j];
n.a[j]=(n.a[j]+ys*mo)/2;
ys=(ys*mo+x)&1;
}
while(!n.a[n.w]&&n.w>1)n.w--;
}
fo(i,0,99)ans=ans+g[tot][i];
printf("%d",ans.a[ans.w]);
fd(i,ans.w-1,1){
xx=log(ans.a[i])/log(10)+1;
fo(j,1,9-xx)putchar('0');
printf("%d",ans.a[i]);
}
return 0;
}