题目大意:
令
s
u
m
(
i
)
sum(i)
sum(i)表示
i
i
i在二进制表示下有多少位是
1
1
1
给定一个正整数
n
n
n,求
∏
i
=
1
n
s
u
m
(
i
)
\prod_{i=1}^{n}sum(i)
∏i=1nsum(i)
m
o
d
mod
mod
10000007
10000007
10000007
n ≤ 1 0 15 n≤10^{15} n≤1015
分析:
考虑数位
d
p
dp
dp,
设
f
i
,
0
/
1
,
j
f_{i,0/1,j}
fi,0/1,j表示二进制下的
i
i
i位,填了
j
j
j个
1
1
1,
0
/
1
0/1
0/1为当前是否已经与
n
n
n的后
i
i
i位一致时(
1
1
1为一致,
0
0
0则反之),这样的数的个数
从后往前转移,
因为
n
n
n为正整数,所以初值为:
f
1
,
0
,
0
=
1
,
f
1
,
1
,
1
=
1
f_{1,0,0}=1,f_{1,1,1}=1
f1,0,0=1,f1,1,1=1
f
i
+
1
,
l
,
m
+
=
f
i
,
j
,
k
f_{i+1,l,m}+=f_{i,j,k}
fi+1,l,m+=fi,j,k,假如我在第
i
+
1
i+1
i+1位放下了一个数
c
c
c
当
j
=
1
j=1
j=1且
c
=
n
c=n
c=n的倒数第
i
+
1
i+1
i+1位,
l
l
l即为
1
1
1,反之为
0
0
0
当
c
=
n
c=n
c=n的时候
m
=
k
+
1
m=k+1
m=k+1否则
m
=
k
m=k
m=k
然后注意一下当
j
=
1
j=1
j=1时,第
i
+
1
i+1
i+1位只能填
[
0
,
n
的
倒
数
第
i
+
1
位
]
[0,n的倒数第i+1位]
[0,n的倒数第i+1位]
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
#define N 65
using namespace std;
typedef long long ll;
const int mo = 10000007;
ll sum[N], f[N][2][N], n;
int num[N], len;
void Pre(ll x)
{
len = 0;
while (x) num[++len] = x & 1, x >>= 1;
for (int i = 1; i * 2 <= len; i++) swap(num[i], num[len - i + 1]);
}
ll ksm(ll x, ll y)
{
ll rp = 1;
for (; y; y >>= 1)
{
if (y & 1) rp = rp * x % mo;
x = x * x % mo;
}
return rp;
}
int main()
{
scanf("%lld", &n);
Pre(n);
f[1][0][0] = f[1][1][1] = 1;
for (int i = 1; i < len; i++)
for (int j = 0; j <= 1; j++)
for (int k = 0; k <= i; k++)
{
int lim = j ? num[i + 1] : 1;
for (int l = 0; l <= lim; l++)
f[i + 1][(j && l == num[i + 1]) ? 1 : 0][l ? k + 1 : k] += f[i][j][k];
}
for (int i = 0; i <= 1; i++)
for (int j = 1; j <= len; j++) sum[j] += f[len][i][j];
ll ans = 1;
for (int i = 1; i <= len; i++)
ans = ans * ksm(i, sum[i]) % mo;
printf("%lld\n", ans);
return 0;
}