BZOJ 1041: [HAOI2008]圆上的整点【高斯素数】

1041: [HAOI2008]圆上的整点

Time Limit: 10 Sec Memory Limit: 162 MB

Description

求一个给定的圆(x^2+y^2=r^2),在圆周上有多少个点的坐标是整数。

Input

只有一个正整数n,n<=2000 000 000

Output

整点个数

Sample Input

4

Sample Output

4

HINT

科普视频

题解

我的想法和黄学长的想法不同,我没看懂他的想法QAQ。
我的想法比较神奇,因为看了这个视频,十分完美的解决了此题:https://www.bilibili.com/video/av12131743/,讨论里也有,就是科普视频的完整版(我也不知道BZOJ里为什么只能看5分钟)。
视频有点长,但是我还是建议看一下,十分有帮助。
我继续视频中讲到的知识,解决这道题。
显然是要将n拆成素数的积,但是题目给的是半径r=r2
所以我们要求r2的素数积。
那么怎么求呢?
r=p1a1p2a2p3a3...pkak
r2=(p1a1p2a2p3a3...pkak)2=p12a1p22a2p32a3...pk2ak
所以只需要求r的质因子然后2就可以了。
然后我们要求

X(n)={1(nmod4==1)1(nmod4==3)0(nmod2==0)

对于r2,没有任何质因子只出现奇数次的情况,所以只需要判nmod4==1的情况,直接(ai+1)就可以了。
当然,我们求r,所以只需要筛到r就可以了,但是有情况是r本身是一个大素数,或是一个大素数*小素数,所以晒完之后再特判一下就可以了。

代码如下

#include<cmath>
#include<cstdio>
#define LL long long
using namespace std;
LL n,m,Ans=1,p[500005];
bool vis[500005];
void make_p(){
    int Len=500000;
    vis[0]=vis[1]=1;
    for(int i=2;i<=Len;i++){    
        if(!vis[i]) p[++p[0]]=i;
        for(int j=1;j<=p[0]&&i*p[j]<=Len;j++){
            vis[i*p[j]]=1;
            if(!(i%p[j])) break;
        }
    }
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("prob.in","r",stdin);
    freopen("prob.out","w",stdout);
    #endif
    make_p();
    scanf("%lld",&n);
    while(!(n&1)) n/=2;
    for(int i=1;i<=p[0]&&p[i]<=n;i++)
    if(!(n%p[i])){
        LL Num=0;
        while(!(n%p[i])) Num++,n/=p[i];
        Num<<=1;
        if(p[i]%4==1) Ans*=Num+1;
    }
    if(n>1) if(n%4==1) Ans*=3;
    printf("%lld\n",Ans*4);
    return 0;
}
posted @ 2018-05-03 16:14  XSamsara  阅读(140)  评论(0编辑  收藏  举报