题意
设 ( ext{sum}(i)) 表示 (i) 的二进制表示中 (1) 的个数。给出一个正整数 (N) ,求 (prod_{i=1}^{N} ext{sum}(i)) 。
思路
换一种角度看这个乘积,会发现就相当于统计出 (1sim N) 中 1 的个数为 (k) 的数量 (cnt_k) ,然后 (prod k^{cnt_k}) 即可。
(怎么那么水啊,这都什么垃圾紫题,题白挑了)为了让这道题更有价值,代码实现非常的神仙。Orz粉兔。
粉兔的代码看了很久才理解……luogu上至今没有看到公开的详解。
这里注释的是我认为正确的理解,若有差错还请指正。
代码
#include <cstdio>
#define ll long long
const ll mod=1e7+7;
ll n,ans=1,cnt,f[50];
ll power( ll a,ll b )
{
ll res=1;
for ( ; b; b>>=1,a=a*a%mod )
if ( b&1 ) res=res*a%mod;
return res;
}
int main()
{
scanf( "%lld",&n );
cnt=0; f[0]=0;
for ( int len=49; ~len; --len )
{
for ( int i=49; i; --i )
f[i]+=f[i-1];
if ( n>>len&1 ) f[cnt]++,cnt++;
//cnt记录的是除了现在这一位,之前有的1的个数,f[cnt]++表示,这一位的1产生了一种使得前面的1全部能取到的方案。
}
f[cnt]++; //加上本身
//之前一直想不明白,如果这样枚举,为什么能直接从49开始。
//一开始的想法是预支最高位的1,这样当前每次加一位就能取1,对应 f[i-1] 到 f[i] 的转移
//但是这样有个问题,就是最高位没有1了怎么办,这样预支无效,答案就会偏大
//后来发现,关键在外层循环。当位数大于二进制下n的位数的时候,f始终为0,最后一句if 不会执行,也就不会出现上述问题。
//一旦开始累加出现了值,那么一定就是有高位可以预支了。否则 if 中的等号不会成立。
for ( int i=1; i<=49; ++i )
ans=ans*power( i,f[i] )%mod;
printf( "%lld",ans );
return 0;
}