注意到n为奇数时,最大奇数因子就是其本身
当n为偶数时,对n不断/2,得到的第一个奇数就是n的最大奇数因子
证明:任何一个数可以表示为 的形式,其中 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
那么写出列表观察是否有规律,从而优化算法
显然
odd为,sn为
even
当n为奇数,f(n)组成为个奇数项和个偶数项
当n为偶数,f(n)组成为个奇数项和个偶数项
由
g(1)=1 | a1=1 | s1=1 | 1-1 |
g(3)=3 | a2=3 | s2=4 | 3-2 |
g(5)=5 | a3=5 | s3=9 | 5-3 |
g(n)=n | an=2*n-1 | sn=(n*(a1+an))/2=n^2 | n=2*n-1 |
由映射关系可得n=(n+1)/2,代入得到g(n)对应sn=
注意力好的朋友可以应该发现sn实际上可以由上文红色部分一眼丁真
那么同理n为偶数时,奇数项对应和为
现在我们优化了奇数项的求和,可以写出如下代码(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
不是他有病吧???
这说明我们还要继续优化偶数的处理
对于“/”很熟悉的话,马上可以发现
eg:n为6时
n为偶数时,奇数项对应和为
n为奇数,奇数项对应和为
n为奇数,奇数项对应和为
eg: n为5时
我们可以写出如下代码(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,显然是足够的。
(注意换行符)