您的位置:首页 > 其它

[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。。)

代码:

#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)。

②对于那种只有一个输入的题,如果实在不会做了,可以找规律嘛!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: