求前
个数因数和的三种方法。
纯暴力,对每个数,遍历
时间复杂度:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+5;
#define mst(a) memset(a,0,sizeof a)
int main(){
int n,ans=0,j;
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(j=1;j*j<=i;j++)
if(i%j==0) ans+=(j+i/j);
j--;
if(j*j==i) ans-=j;
}
printf("%d\n",ans);
return 0;
} 考虑每个因数对答案的贡献,显然对1有
次贡献,
有
次贡献,依次类推,时间复杂度:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+5;
#define mst(a) memset(a,0,sizeof a)
int main(){
int n,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
ans+=i*(n/i);
printf("%d\n",ans);
return 0;
} 因为时,对应
个
.同样当
,对应
个
。
所以可以在中同时对
和
进行计算贡献。
显然对于固定左边的,贡献为
.
而对于固定右边的.这里的
也从
开始遍历,这里的
也就是程序里的
。因为这样可以避免算重,这样算的贡献的是左边的所有的大于
的
的贡献。这样大于
的
产生的贡献加上小于等于
的
产生的贡献就等于答案的贡献,即
。注意最后需要去重一下,因为可能存在
的情况。
这样时间复杂度就能到了。
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll ans,n,i;
int main(){
while(~scanf("%lld",&n)){
ans=0,i=1;
for(i=1;i*i<=n;i++){
ans+=i*(n/i);
ll r=n/i,l=n/(i+1)+1;
ans+=(l+r)*(r-l+1)/2*i;//区间[l,r],[n/i]=i时 的贡献.
}
i--;
if(i==(n/i)) ans-=i*(n/i);//去重
printf("%lld\n",ans);
}
return 0;
} 
京公网安备 11010502036488号