题意:
问[L,R]内所有数的因子的数量和
题解:
如果传统暴力做肯定不行
我们来找找规律:
数字: 因子数目 1~n的因子数和
1 1 1
2 2 3=2+1/
3 2 5=3+1+1
4 3 8=4+2+1+1
5 2 10=5+2+1+1+1
6 4 14=
7 2 16=
8 4 20=
9 3 23=9+4+3+2+1+1+1+1+1
有发现什么规律?
可以推出:1—n的因子数和 =∑ (int)n/i (i是从1到n)
问我咋推出来的?
额。。“直觉”算能力吗?我在纸上把因子数和进行拆分就发现这样的规律
但是注意题目中的数据是1e12,感觉直接做还是不准,,O(n)肯定不行,此时就要往O(log n)或者O(√n)的方向去想
∑ (int)n/i其实是函数y=n/x的一个离散和
而这个函数是关于y=x对称,对称点为(√n,√n),图像面积是对称的,所以我们只需要求出√n数据范围内的答案即可,然后乘以2减去√n√n,其实就是去掉重复的正方形面积,正方形边长是√n,这样一来复杂度不就降低了,1e12就没问题了
答案就是 ans2 - t *t
t=sqrt(n)
小于sqrtN的因子在所有数中有多少:∑N/i (i=1,…,sqrtN)
大于sqrtN的因子在所有数中有多少: ∑N/i (i=1,…,sqrtN)
但是两者有重复部分
“因子对”里的数都小于sqrtN的那些因子其实被重复加了一遍,多加了Q * Q个因子
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;//用long long
ll work(ll n)//计算一个数的因子之和
{
ll ans=0;
ll t=sqrt(double(n));//根号n
for(int i=1;i<=t;i++){
ans=ans+n/i;
}
ans=ans*2-t*t;//公式求和
}
int main ()
{
ios::sync_with_stdio(false);
ll n,m;
cin>>n>>m;
cout<<work(m)-work(n-1)<<endl;//从n-m的,跟前缀和差不多原理
return 0;
}