【题解】P4317 花神的数论题,关于luogu题解粉兔做法的理解

link

题意

\(\text{sum}(i)\) 表示 \(i\) 的二进制表示中 \(1\) 的个数。给出一个正整数 \(N\) ,求 \(\prod_{i=1}^{N}\text{sum}(i)\)

思路

换一种角度看这个乘积,会发现就相当于统计出 \(1\sim 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;
}
posted @ 2020-11-03 18:52  MontesquieuE  阅读(139)  评论(0编辑  收藏  举报