好像问题挺简单的,来看看数据范围吧……
有没有点刚开始很欣喜后来有些恼火的感觉?那就对啦
:诶,这题我会n^2写法,20分呀,啊哦
:诶,我傻了,每个数求一下因数,n*sqrt(n)可以写。(看下数据范围,还是20)
:啊!我想到了nlogn的写法!!(看一下数据范围,这尼玛怎么还是20啊~~)
蒟蒻实在想不到更优秀的算法了,就先讲一下我们nlogn的“优秀”算法吧。
注意,我们之前在每个数都进行根号求因数的时候,其实重复了很多次。就是说你枚举根号n个数里面有很多数不是因数,没有什么用。我们要充分利用每一个数,减少不必要的枚举。所以,应该考虑每个数能对哪些数产生贡献。
如果当前数的因数个数确定了,那么它能产生的所有贡献都在是它的整数倍的数上。因此我们可以枚举当前数的所有倍数,把它的因数个数加到这个倍数的答案里。
那么我们如何知道当前数的因数个数呢?还要根号n求吗?其实不然,如果按照我们刚才说的筛法,我们对每个数再记录一下它被筛到的次数,那么当你递推到当前数的时候,你已经知道了它的因数个数,就是被筛的次数+1(1是它本身)。
复杂度怎么算?
每一个数枚举倍数,复杂度是n/i的,那么就是n/1+n/2+n/3+n/4+......+n/n,有同学说有点像n^2,别急。把n提出来,变成n*(1+1/2+1/3+1/4+...+1/n),(1+1/2+1/3+1/4+...+1/n)是调和级数,我们可以认为它是log的复杂度,因此本做法复杂度nlogn。
可是明明利用每个数那么充分了,怎么还是20分啊QAQ。说明我们的思路需要转化。(不过如果需要打表的同学,本算法算是最优秀的了)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,x,y,z;ll ans; //wa的教训告诉我一定全部变量使用longlong
int main(){freopen("divisor.in","r",stdin);freopen("divisor.out","w",stdout);scanf("%lld",&n);ll l=pow(1.0*n,0.33333333)+1; //毒瘤题目卡精度,多加几个3ll r;for(x=1;x<=l;++x){r=sqrt(1.0*(1.0*n/x))+1;for(y=x;y<=r;++y){z=n/(x*y);while(x*y*z>n) z--;if(z>=y){ans+=1LL*(z-y+1)*6;}}}r=sqrt(1.0*n)+1;for(x=1;x<=r;++x){z=n/(x*x);if(z>0){if(z>=x) ans-=1LL*(z-1)*3;else ans-=1LL*z*3;}}for(x=1;x<=l;++x){int temp=x*x*x;if(temp<=n) ans-=5;}printf("%lldn",ans);return 0;
}
本文发布于:2024-02-05 00:58:49,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170720432461594.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |