一月17日新生冬季练习赛解题报告B.函数问题
B题的解题报告非常值得写,其解题思路让人叹为观止,比赛的时候没有一个人ac,其实我想用纯暴力劫一下试试呢,幸好没试,不然会挂的很惨
题目是这样的:
B.函数问题 | |||||
|
|||||
Description | |||||
定义一个函数f(x),意思为x%(a*b)==0的a,b对数...如 f(1)=1:1*1 f(2)=3:1*1,1*2,2*1 f(3)=3:1*1,1*3,3*1 f(4)=6:1*1,1*2,2*1,2*2,1*4,4*1 ................. 现在输入一个正整数N(N<=10^11)..问f(1)+f(2)+f(3)+f(4)+....f(N)的值... |
|||||
Input | |||||
多组输入数据。
每组数据输入一个n。
|
|||||
Output | |||||
每组数据输出问题结果,格式为Case #: ans。 |
|||||
Sample Input | |||||
1 3 6 10 15 21 28 |
|||||
Sample Output | |||||
Case 1: 1 Case 2: 7 Case 3: 25 Case 4: 53 Case 5: 95 Case 6: 161 Case 7: 246 |
这种题呢,看上去是求每个子问题,然后求和,做的过程你会发现单个子问题是没法全部求出来的,就算求出来也没有数组能够存的下(10^11) 就算存的下光是最后的遍历也会把时间全都用完 所以我们要整体的考虑 f(1)加到f(n)的和
那么怎么整体考虑呢?
我们看一下给的前三组例子:
f(1)=1:1*1
f(2)=3:1*1,1*2,2*1
f(3)=3:1*1,1*3,3*1
求的是 f(1)+f(2)+f(3)
对于1 有(1,1) 1/(1*1)=1
对于2 有(1,1) (1,2) (2,1) 2/(1*1)=2 2/(1*2)=1 2/(2*1)=1
对于3 有(1,1) (1,3) (3,1) 3/(1*1)=3 3/(1*3)=1 3/(3*1)=1
我们把每两个相乘的数 左边的看做a 右边的看做 b 把相除的结果看做 c
把所有分母都移到右边;
接下来这一步有些跳跃,需要自己想明白
那么:f(1)+f(2)+f(3)的问题就可以转换成a*b*c<=3的所有abc的种数
应用到所有就是 a*b*c<=n的所有abc的组合数 其中abc科翔等或者不等
接下来的问题就是求多少组了:
可以分为a b c 都相等 、 a b c 中有两个相等和abc都不想等这三种情况
下面我边写代码边解释:
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
long long int n,a,b,c;///数据太大所以要用long long
std::ios::sync_with_stdio(false);///加速cin cout 也可以直接使用c语言的输入输出
while(cin>>n)
{
long long int ans = 0;///用来记录总种类数 ans=answer
for(a = 1; a*a*a <= n; a++)///abc都相等的情况
ans++;
for(a = 1; a*a <= n; a++)///abc中有两个相同 相同的用a表示 另一个用b表示
{
b=n/a/a;///b并不是指的实际的b值 而是b的取值范围是1-b换句话说就是b的种类数
if(b >= a)
ans += (b-1)*3;///因为a跟b一定是不相等的 所以 要从 1--b中减去 和a相等的那种情况
///至于乘3 因为 aab的组合数 为 3
else
ans += b*3;///这种情况是 b的范围是 a之内 所以和a相等
}
///地下是三者都不想等的情况 只要a从1遍历到n b从a+1遍历到n c从b+1遍历到n 再乘以组合数6就可以了^_^
for(a = 1; a*a <= n; a++)///abc都不相等的情况
{
for(b = a+1; a*b <= n; b++)
{
c=n/a/b;
if(c>b)
ans+=(c-b)*6;///不用解释了吧
else
break;
}
}
cout<<ans<<endl;
}
return 0;
}