整数分解为2的幂

题号是\(\text{51nod}\) 13831048

\(n\le 10^6\)

这档分\(O(n\log n)\)或者\(O(n)\)都可以。

\(\log\)的是用\(2\)的幂跑一次完全背包

线性的考虑每个数从为空的序列开始是通过\(+1\)或者集体\(\times 2\)得到的。

\(f[i]=(i\&1)?0:f[i/2]+f[i-1]\)

\(n\le 10^{30}\)

我们先考虑每个二的整数幂有多少种方法凑出来。

\(f[i][j]\)表示最大数是\(2^j\)的数凑出\(2^i\)的方案数。

如果\(f[i][j]=\sum_{k=0}^jf[i-1][k]\times f[i-1][j]\)

显然在会算重

我们强行让后面方案用的数都大于前面的

也就是去掉小于\(2^k\)产生的贡献,\(f[i-k-1][j-k]\)也就是所有数除了\(2^k\)

\(f[i][j]=\sum_{k=0}^jf[i-1][k]\times f[i-k-1][j-k]\)

我们要利用\(f\)来计算答案

我们考虑\(2\)进制下,\(g[i][j]\)表示用最大数是\(2^j\)的数凑前\(i\)位的方案数

\(g[i][j]=\sum_{k=0}^jg[i-1][k]\times f[i-k][j-k]\)

注意某一位是\(1\)的时候才需要更新\(g\)数组,应该记录一下\(g\)的第一维更新了多少次。

其实还没有做完...

因为出题人十(sang)分(xin)友(bing)好(kuang)不给模数,所以你还得写个压位高精。

说实话这东西我找了好久的板子,因为我不会写高精度...

\(\text{oi-wiki}\)上面那个封装好的高精度有点问题,高精度乘法模板都过不了

高精度模板放在代码里面了

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
struct Wint:vector<ll>
{
const static ll BIT=1e8;
Wint(ll n=0) {push-back(n);check();}
Wint& operator=(const char* num)
{
int Len=strlen(num)-1; clear();
for(int i=Len;i>=0;i-=9)
{
push-back(0); ll w=1;
for(int j=i;j>i-9&&j>=0;--j)
back()+=(num[j]^48)*w,w*=10;
}
return *this;
}
Wint& check()
{
while(!empty()&&!back()) pop-back();
if(empty()) return *this;
for(int i=1;i<size();++i)
(*this)[i]+=(*this)[i-1]/BIT,
(*this)[i-1]%=BIT;
while(back()>=BIT)
{
push-back(back()/BIT);
(*this)[size()-2]%=BIT;
}
return *this;
}
};
bool operator<(Wint a,Wint b)
{
if(a.size()!=b.size()) return a.size()<b.size();
for(int i=a.size()-1;i>=0;--i)
if(a[i]!=b[i]) return a[i]<b[i];
return 0;
}
bool operator>(Wint a,Wint b) {return b<a;}
bool operator<=(Wint a,Wint b) {return !(a>b);}
bool operator>=(Wint a,Wint b) {return !(a<b);}
bool operator!=(Wint a,Wint b) {return a<b||b<a;}
bool operator==(Wint a,Wint b) {return !(a<b)&&!(b<a);}
Wint& operator+=(Wint &a,Wint b)
{
if(a.size()<b.size()) a.resize(b.size());
for(int i=0;i<b.size();++i) a[i]+=b[i];
return a.check();
}
Wint operator+(Wint a,Wint b) {return a+=b;}
Wint& operator-=(Wint &a,Wint b)
{
for(int i=0;i<b.size();a[i]-=b[i],++i)
if(a[i]<b[i])
{
int j=i+1;
while(!a[j]) ++j;
while(j>i) --a[j],a[--j]+=Wint::BIT;
}
return a.check();
}
Wint operator-(Wint a,Wint b) {return a-=b;}
Wint operator*(Wint a,Wint b)
{
if(a.empty()&&b.empty()) return a;
Wint n; n.assign(a.size()+b.size()-1,0);
for(int i=0;i<a.size();++i)
for(int j=0;j<b.size();++j)
n[i+j]+=a[i]*b[j];
return n.check();
}
Wint& operator*=(Wint &a,Wint b) {return a=a*b;}
Wint operator/(Wint a,int b)
{
Wint n; bool wp=0; ll t=0;
for(int i=a.size()-1;i>=0;--i)
{
t=t*Wint::BIT+a[i];
if(wp||t/b) wp=1,n.push-back(t/b);
t%=b;
}
reverse(n.begin(),n.end());
return n;
}
Wint& operator/=(Wint &a,int b) {return a=a/b;}
void readX(Wint &n) {char s[1000]; scanf("%s",s); n=s;}
void writeX(Wint n)
{
if(n.empty()) {putchar('0'); return;}
int Len=n.size()-1; printf("%lld",n[Len]);
for(int i=Len-1;i>=0;--i) printf("%08lld",n[i]);
}

typedef --int128 i128;
i128 n;
Wint f[142][142], g[142][142];

i128 read() {
char ch = getchar();
i128 w = 0;
while (ch > '9' || ch < '0') ch = getchar();
while (ch <= '9' && ch >= '0') {
w = w * 10 + ch - 48;
ch = getchar();
}
return w;
}

int main() {
ios::sync-with-stdio(false);
n = read();
int limit = 100;
f[0][0] = 1;
for (int i = 1; i <= limit; i++)
for (int j = 0; j <= i; j++) {
if (j == i) f[i][j] = 1;
else for (int k = 0; k <= j; k++) f[i][j] = f[i][j] + f[i - 1][k] * f[i - 1 - k][j - k];
}
g[0][0] = 1;
int cnt = 0;
for (int i = 0; i <= limit; i++) {
if ((n >> (i128)(i)) & 1) {
++cnt;
for (int j = 0; j <= i; j++) {
for (int k = 0; k <= j; k++) g[cnt][j] = g[cnt][j] + g[cnt - 1][k] * f[i - k][j - k];
}
}
}
Wint res;
for (int i = 0; i <= limit; i++) res = res + g[cnt][i];
writeX(res);
return puts(""), 0;
}