月之数

月之数

求n位二进制数(不含前导0)中所有的1出现的次数,\(n\leq 20\)

法一:递推

想到数位递推,故采取数位递推策略,显然设\(g_i\)为i位二进制数(不含前导0)中1出现的次数,设\(f_i\)为i位以内的二进制数(不含前导0)的1出现的次数,自然有

\[f_i=2f_{i-1}+2^{i-1} \]

\[g_i=f_{i-1}+2^{i-1} \]

边界:\(f_0=g_0=0\)

顺便提一下,这个递推式子,你无论用数列的知识求出通项公式,还是直接利用矩阵快速幂做到\(O(log_2^n)\)都可以,只是此题数据范围不需要。

参考代码:

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define ll long long
using namespace std;
ll f[21],g[21];
il void prepare();
int main(){
	int lsy,n;prepare();
	scanf("%d",&lsy);
	while(lsy--)
		scanf("%d",&n),
			printf("%lld\n",g[n]);
	return 0;
}
il void prepare(){
	for(int i(1);i<=20;++i)
		f[i]=2*f[i-1]+(1<<i-1),
			g[i]=(1<<i-1)+f[i-1];
}

法二:组合意义

注意到不含前导0,不妨钦定第n位填1,然后--n,这样第n+1位累加的答案就只有\(2^{n-1}\),然后接下来的问题就是任意个1在n个位置自由排列的1的个数,不妨枚举1的个数,再结合前面第n+1位的答案,有最终答案

\[C_{n}^1+2C_{n}^2+...+nC_{n}^n+2^{n-1} \]

这个式子\(O(n^2)\)预处理出组合数,即可直接暴力求,数据范围也支持,但是我们不止步与此,因为\(C_n^1+C_n^2+...+C_n^n=2^{n}-1\),而且注意到这个式子的形式,我们可以考虑等差数列乘以另一个数列的一般做法,做到\(O(log_2^n)\),这里就不多说了,你可以参考我的基础算法知识点小结中的基本模型的有关内容

参考代码:

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define ll long long
using namespace std;
ll c[21][21],ans,base[21]={1};
int main(){
	int n,lsy;scanf("%d",&lsy);
	for(int i(0),j;i<=20;++i)
		for(j=c[i][0]=1;j<=i;++j)
			c[i][j]=c[i-1][j]+c[i-1][j-1];
	for(int i(1);i<=20;++i)
		base[i]=base[i-1]<<1;
	while(lsy--){
		ans&=0,scanf("%d",&n),--n;
		for(int i(1);i<=n;++i)
			ans+=c[n][i]*i;
		printf("%lld\n",ans+base[n]);
	}
	return 0;
}
posted @ 2019-07-18 18:21  a1b3c7d9  阅读(179)  评论(0编辑  收藏  举报