274: 函数求值

c0a7479f4acf4d32bc227396648b8b3a.png

注意到n为奇数时,最大奇数因子就是其本身

当n为偶数时,对n不断/2,得到的第一个奇数就是n的最大奇数因子

证明:任何一个数可以表示为eq?%242%5Ek%20%5Ctimes%20m%24 的形式,其中 k 是非负整数,m 是一个奇数。因此,对一个数不断地除以 2,相当于将其中的 2 因子全部提取出来,直到将 2 全部除尽为止。

我们可以写出如下代码(O(n^2*m))

#include<stdio.h>
int main()
{
	int n;
	while (~scanf("%d",&n)!=EOF)
	{
		int sum=0;
		for (int i=1;i<=n;i++)
		{
			if (i%2)
				sum+=i;
			else
			{
				int i1=i;
				while(!(i1%2))
				{
					i1/=2;
				}
				sum+=i1;
			}
		}
		printf("%d",sum);
	}
	return 0;
}

Time Limit Exceeded

那么写出列表观察是否有规律,从而优化算法

eq?f%281%29%3Dg%281%29%3D1

eq?f%282%29%3Dg%281%29&plus;g%282%29%3D1&plus;g%28%5Cfrac%7B2%7D%7B2%7D%29

eq?f%283%29%3Dg%281%29&plus;g%282%29&plus;g%283%29%3D1&plus;g%28%5Cfrac%7B2%7D%7B2%7D%29&plus;3

eq?f%284%29%3Dg%281%29&plus;g%282%29&plus;g%283%29&plus;g%284%29%3D1&plus;g%28%5Cfrac%20%7B2%7D%7B2%7D%29&plus;3&plus;g%28%5Cfrac%20%7B4%7D%7B2%7D%29

eq?f%285%29%3Dg%281%29&plus;g%282%29&plus;g%283%29&plus;g%284%29&plus;g%285%29%3D1&plus;g%28%5Cfrac%20%7B2%7D%7B2%7D%29&plus;3&plus;g%28%5Cfrac%20%7B4%7D%7B2%7D%29&plus;5

eq?f%286%29%3Dg%281%29&plus;g%282%29&plus;g%283%29&plus;g%284%29&plus;g%285%29&plus;g%286%29%3D1&plus;g%28%5Cfrac%20%7B2%7D%7B2%7D%29&plus;3&plus;g%28%5Cfrac%20%7B4%7D%7B2%7D%29&plus;5&plus;g%28%5Cfrac%20%7B6%7D%7B2%7D%29

显然eq?f%28n%29%3D%5Csum%20odd&plus;%5Csum%20even

odd为eq?1%2C3%2C5%2C...%2C2*n-1,sn为eq?n%5E%7B2%7D

even

当n为奇数,f(n)组成为eq?%5Cfrac%7Bn&plus;1%7D%7B2%7D个奇数项和eq?%5Cfrac%7Bn-1%7D%7B2%7D个偶数项

当n为偶数,f(n)组成为eq?%5Cfrac%7Bn%7D%7B2%7D个奇数项和eq?%5Cfrac%7Bn%7D%7B2%7D个偶数项

g(1)=1a1=1s1=11-1
g(3)=3a2=3s2=43-2
g(5)=5a3=5s3=95-3
g(n)=nan=2*n-1sn=(n*(a1+an))/2=n^2n=2*n-1

由映射关系可得n=(n+1)/2,代入得到g(n)对应sn=eq?%28%5Cfrac%7Bn&plus;1%7D%7B2%7D%29%5E2%3D%5Cfrac%7B%28n&plus;1%29%5E2%7D%7B4%7D

注意力好的朋友可以应该发现sn实际上可以由上文红色部分一眼丁真

那么同理n为偶数时,奇数项对应和为eq?%28%5Cfrac%7Bn%7D%7B2%7D%29%5E2%3D%5Cfrac%20%7Bn%5E2%7D%7B4%7D

现在我们优化了奇数项的求和,可以写出如下代码(O((n/2)^2*m))

#include<stdio.h>
int main()
{
	int n;
	while (~scanf("%d",&n)!=EOF)
	{
		int sum=0;
		if (n%2)
		{
			sum+=(n+1)*(n+1)/4.0;
			for (int i=2;i<=n-1;i+=2)
			{
				int i1=i;
				while(!(i1%2))
				{
					i1/=2;
				}
				sum+=i1;
			}
		}
		else
		{
			sum+=(n*n)/4.0;
			for (int i=2;i<=n;i+=2)
			{
				int i1=i;
				while(!(i1%2))
				{
					i1/=2;
				}
				sum+=i1;
			}
		}
		printf("%d\n",sum);
	}
	return 0;
}

Time Limit Exceeded

不是他有病吧???

这说明我们还要继续优化偶数的处理

dab8b417e20e4f8d94a7b5c0d5371222.png

对于“/”很熟悉的话,马上可以发现

eq?f%28n%29%3D%5Csum%20odd&plus;f%28%5Cfrac%20%7Bn%7D%7B2%7D%29

eg:n为6时

eq?f%286%29%3D%5Cfrac%7B%286%29%5E2%7D%7B4%7D&plus;f%28%5Cfrac%7B6%7D%7B2%7D%29        n为偶数时,奇数项对应和为eq?%5Cfrac%20%7Bn%5E2%7D%7B4%7D

eq?f%283%29%3D%5Cfrac%7B%283&plus;1%29%5E2%7D%7B4%7D&plus;f%28%5Cfrac%7B3%7D%7B2%7D%29        n为奇数,奇数项对应和为eq?%5Cfrac%7B%28n&plus;1%29%5E2%7D%7B4%7D

eq?f%281%29%3D1                          n为奇数,奇数项对应和为eq?%5Cfrac%7B%28n&plus;1%29%5E2%7D%7B4%7D

eg: n为5时

eq?f%285%29%3D%5Cfrac%7B%285&plus;1%29%5E2%7D%7B4%7D&plus;f%28%5Cfrac%7B5%7D%7B2%7D%29        

eq?f%282%29%3D%5Cfrac%7B2%5E2%7D%7B4%7D&plus;f%28%5Cfrac%7B2%7D%7B2%7D%29

eq?f%281%29%3D1

我们可以写出如下代码(O(n*m))

#include<stdio.h>
int main()
{
	long long n;
	while (scanf("%lld",&n)!=EOF)
	{
		long long sum=0;
		while(n)
		{
			if (n%2)
				sum+=(n+1)*(n+1)/4;
			else
				sum+=(n*n)/4;
			n/=2;
		}
		printf("%lld\n",sum);
	}
	return 0;
}

完美AC

这里对n的输入也要采用long long类型,我们知道int是2E9,第一眼看10^8够用,第二眼发现使用了求和公式,如果n取10^8,(10^8)^2/4≈10^16,显然int不够了,所以我们使用long long,long long为4E18,显然是足够的。

(注意换行符)

 

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值