[bzoj4544]椭圆上的整点 解题报告
2016-04-19 17:39
295 查看
先来看傻逼的做法:
稍微化下式子。
x2+3y2=n23y2=(n+x)(n−x)
令g=(n+x,n−x),则有n+x=ga2,n−x=g3b2或n+x=g3b2,n−x=ga2(a,b∈Z).
所以有2n=g(a2+3b2),那么显然有g|2n。所以我们先枚举g,然后再枚举3b2,判断n−3b2是否是一个完全平方数。但是有一个很蛋疼的地方是a2与3b2要互质。如果用gcd来判互质的话。。感觉有点慢。
但是注意到我们为什么要求它们互质呢?因为如果不互质的话,就可以被提到g中去,这样就会计算多次了。那么我们就干脆让g不含平方因子就可以了。因为如果g含有平方因子,那么它就可以被放到外面来,成为一个在g不含平方因子而a2,3b2不互质的情况。
这样的话最多有11个质因子,就需要枚举211个g,然后每次花费n√的代价判断。但是这个n√在枚举的过程中其实下降的很快,因为要除约数嘛。
实际测试,最坏情况要跑1.6s左右,一个点时限3s的话是可以搞定的。
膜拜大爷的做法:
打表可以发现,其实答案/2(去掉对称的情况)后是一个积性函数。f(pk)=⎧⎩⎨⎪⎪212k+1,p=2,p≡5mod6,p≡1mod6
(不知道这么神的规律是怎么找出来的orz。。)
代码:
总结:
①开变量之前注意想好它的范围(有没有爆int)。
②对于那种只有一个输入的题,如果实在不会做了,可以找规律嘛!
稍微化下式子。
x2+3y2=n23y2=(n+x)(n−x)
令g=(n+x,n−x),则有n+x=ga2,n−x=g3b2或n+x=g3b2,n−x=ga2(a,b∈Z).
所以有2n=g(a2+3b2),那么显然有g|2n。所以我们先枚举g,然后再枚举3b2,判断n−3b2是否是一个完全平方数。但是有一个很蛋疼的地方是a2与3b2要互质。如果用gcd来判互质的话。。感觉有点慢。
但是注意到我们为什么要求它们互质呢?因为如果不互质的话,就可以被提到g中去,这样就会计算多次了。那么我们就干脆让g不含平方因子就可以了。因为如果g含有平方因子,那么它就可以被放到外面来,成为一个在g不含平方因子而a2,3b2不互质的情况。
这样的话最多有11个质因子,就需要枚举211个g,然后每次花费n√的代价判断。但是这个n√在枚举的过程中其实下降的很快,因为要除约数嘛。
实际测试,最坏情况要跑1.6s左右,一个点时限3s的话是可以搞定的。
膜拜大爷的做法:
打表可以发现,其实答案/2(去掉对称的情况)后是一个积性函数。f(pk)=⎧⎩⎨⎪⎪212k+1,p=2,p≡5mod6,p≡1mod6
(不知道这么神的规律是怎么找出来的orz。。)
代码:
#include<cstdio> #include<iostream> using namespace std; #include<cstring> #include<cmath> #include<algorithm> typedef long long LL; const int T=10+5; const LL N=1e12+5; const int R=1e6+10; int prime[R]; bool p[R+5]; const int Log=12; LL dvs[Log]; bool check(LL x){ //printf("check(%I64d)=%d\n",x,(LL)pow((int)sqrt(x),2)==x); return (LL)pow((int)sqrt(x),2)==x; } LL query(LL n){ //printf("query(%I64d)\n",n); int ans=0; for(LL i=1;3*i*i<n;++i)ans+=check(n-3*i*i); //printf("ans=%d\n",ans); return ans; } int main(){ freopen("bzoj_4544.in","r",stdin); //freopen("bzoj_4544.out","w",stdout); for(int i=2;i<=R;++i){ if(!p[i])prime[++prime[0]]=i; for(int j=1;j<=prime[0]&&i*prime[j]<=R;++j){ p[i*prime[j]]=1; if(i%prime[j]==0)break; } } int t; scanf("%d",&t); int dtot; LL n,tmp,prod; LL ans; while(t--){ cin>>n; n<<=1; dtot=0; tmp=n; for(int i=1;(LL)prime[i]*prime[i]<=tmp;++i) if(tmp%prime[i]==0){ if(prime[i]!=3)dvs[dtot++]=prime[i]; while(tmp%prime[i]==0)tmp/=prime[i]; } if(tmp!=1&&tmp!=3)dvs[dtot++]=tmp; ans=0; for(int i=1<<dtot;i--;){ prod=1; for(int j=dtot;j--;) if(i>>j&1) prod*=dvs[j]; ans+=query(n/prod); } cout<<((ans<<1|1)<<1)<<endl; } }
总结:
①开变量之前注意想好它的范围(有没有爆int)。
②对于那种只有一个输入的题,如果实在不会做了,可以找规律嘛!
相关文章推荐
- weblogic更改文件不生效问题
- sdut 2882 完全二叉树两结点之间的最小距离
- CA解扰的那点事
- 7种形式的Android Dialog使用举例
- nginx、fastCGI、php-fpm关系梳理
- Servlet技术
- HDU 2546 (01背包)
- 有关Windows免密码登陆远程桌面的两种实现方法
- number to string
- 3D touch的 使用心得
- Tomcat 配置 默认应用 (去掉项目名称、移除项目名称)
- oledb 访问ACCESS
- ubuntu/var/log/下各个日志文件
- 二分查找树
- Android中AccessibilityService(辅助类服务)的用法
- OmniGraffle Tips
- nginx配置location [=|~|~*|^~] /uri/ { … }用法
- 程序员常用的十款软件
- 微软拥抱Linux,着实太晚了
- 法乎其上,得乎其中——从基础出发,写更优化的代码