题解 : 约数研究

这是本蒟蒻第一次写题解,好激动。

我下面介绍**四**种解法 _~~有两种比较神奇~~_

 

------------

# 方法一

先发一下正解吧,具体解释其他题解都有,我就不详细说了。重点是下面三种方法。
```cpp
#include <iostream>
using namespace std;
typedef long long ll;

ll n, ans;

int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
ans += n / i;
}
cout << ans << endl;
return 0;
}
```
时间复杂度:O(n)~~应该是~~

极其~~简陋~~精简,不是吗?

 

------------

# 方法2

但本蒟蒻考试的时候抽风了没有想到。

这道题我看到题解里有许多巨佬已经把各种方法用上了,不过我要介绍一种方法,是当时我考模拟赛时想到的。那就是
# ~~_打表_~~

没错,你没有听错。我当时用暴力做只水了70分,我这时想到了我们机房的一位巨佬打表A了反素数,想着,干脆来一波打表吧~~感觉打表挺好玩的~~。

思路:暴力的时间复杂度是O(n*$\sqrt{n}$)~~打个根号打了5分钟才打出来,第一次写题解~~既然10^6的数据会炸。~~我们不如每10^5个数打一个表,然后暴力求解区间小于10^5的因数。~~

_~~当时考试时我给老师看了一下我的方法,然后老师把嘴里的茶都吐了出来,说:~~_

# 你这个毒瘤啊!

打表生成器:

```cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

#define maxa 1000001
#define int long long
using namespace std;

int ans,n;
int zhi[maxa],vis[maxa];
void y(int x){
int k=sqrt(x);
for(int i=1;i<=k;i++){
if(x%i==0)ans+=2;
}
if(x==k*k)ans--;
}
void fp(){
for(int i=1;i<=n;i++){
y(i);
}
}

main(){
cin>>n;
fp();
cout<<ans;
}
```

 

AC代码:

```cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

#define maxa 1000001
#define int long long
using namespace std;

int ans,n,m;
int zhi[maxa],vis[maxa];
int ans1[]={1166750,2472113,3829833,5221472,6638449,8075504,9529316,10997454,12478206,13970034};//传说中的打表
void y(int x){
int k=sqrt(x);
for(int i=1;i<=k;i++){
if(x%i==0)ans+=2;
}
if(x==k*k)ans--;
}
void fp(){
for(int i=m;i<=n;i++){
y(i);
}
}

main(){
cin>>n;
if(n>=100000){
ans+=ans1[(n/100000)-1];
}
m=n/100000;
m*=100000;
m++;
fp();
cout<<ans;
}
```
时间复杂度:O(n/10~~玄学的10~~*$\sqrt{n/10}$)

不过注意,如果你的测评~~姬~~机不好,可能有一个点会T。(999998)

------------

# 方法三

其实当时考试的时候我本来是用类似于素数筛的思想来做的,但是我WA了。我后来订正了一下。大概就是枚举每个数,如果有它的倍数,则ans+=2。平方数特判一下就可以了。

代码:

```cpp
#include <iostream>
#include <cstdio>
using namespace std;
int n,primes[1000005],pc=0,vis[1000005],cnt[1000005],ans;
int main() {
cin>>n;
for(int i=1;i<=n;i++) cnt[i]=1;
//素数筛
for(int i=2;i<=n;i++) if(cnt[i]<=1) {
primes[pc++]=i;
for(int j=1;j*i<=n;j++) {
vis[i*j]++;
int tmp=j,c=2;
while(tmp%i==0) tmp/=i,c++;
cnt[i*j]*=c;
}
}
for(int i=1;i<=n;i++) ans+=cnt[i];
cout<<ans;
}
```
时间复杂度:O(nlogn).

 

------------

# 方法四

这种方法绝对是前无古人,后无来着的。因为这份代码是我们机房里的国家队卧底。他做到了传说中的
# n方过百万,暴力碾标算!
而且我们的老师重测了几遍,他的代码都跑的飞快,4~5ms,至多59ms。
他正好避过了所有的数据。
## 他,就是
# cpy大巨佬

代码:
(因为他太巨了,所以我怕这份代码吓坏洛谷。这里省略部分)

```
#include<bits/stdc++.h>
using namespace std;
int n;
long long ans[1000005],anss=1,j=1;
int main(){
anss=1;
cin>>n;
for(int i=1;i<=n;i++) ans[i]=1;
for(int i=2;i<=n;i++){
//这里打上马赛克,因为他太巨了!
j=1;
}
for(int i=2;i<=n;i++){
anss+=ans[i];
}
cout<<anss;
}
```

时间复杂度: O(n^2)
经实测,该算法实际速度和方法1不相上下。

 

------------
以上就是本萌新的题解,这是我的第一个题解,写了两个多小时。还望各位神犇多多包容。

 

posted @ 2019-07-29 21:23  WangQT  阅读(235)  评论(1编辑  收藏  举报