NOD 1171 x^2+y^2=N

1171 . 两个数的平方和 V2

时间限制:1 秒 空间限制:65536 KB 分值: 1280

给出一个整数N,将N表示为2个整数i j的平方和(i <= j),如果有多种表示,按照i的递增序输出。

例如:N = 130,130 = 3^2 + 11^2 = 7^2 + 9^2 (注:3 11同11 3算1种)

Input

一个数N(1 <= N <= 10^18)

Output

共K行:每行2个数,i j,表示N = i^2 + j^2(0 <= i <= j)。
如果无法分解为2个数的平方和,则输出No Solution

题解:这题非常全面。

首先 如果一个奇素数p为4k+1形式那么有且仅有一组x,y可以表示为x^2+y^2=p。当然2=1^2+1^2。

那么根据费马平方和定理,如果两个数可以表示成两数平方和的形式那么着两个数的乘积也能表示长平方和的形式,即

首先如果一个数表示成素因子乘积的形式那么4k+3型的素因子p的幂a一定是偶数。偶数幂可以写成

如果N中含有素因子p的幂为奇数,那么直接可以输出no solution了。

接下来就是如何将4k+1形式的素数分解成两个数x,y的平方和了。至于x,y如何求就要分两步。

(1)先找出同余方程的最小正整数解

(2)对进行欧几里德辗转相除运算,记每次的余数为,当满足条件时停止运算,此时的就是x

这样就得到了x,那么y可以通过得到,问题解决。本方法是目前为止发现解此问题最快的方法。

所以将问题转化为求同余方程的最小正整数解

http://algo.ftiasch.com/tag/number-theory/ 粘过来有些奇怪 膜拜一下叉姐

今天的话题是解方程x2≡n(modp),其中p是奇质数。

引理:ap−12≡±1(modp)

证明:由费马小定理,ap−1−1≡(ap−12−1)(ap−12+1)≡0⇒ap−12≡±1

引理:方程有解当且仅当np−12≡1(modp)。

(充分性)xp−1≡1⇒np−12≡(x2)p−12≡1

(必要性)如果方程无解,则可以把1,2,…,(p−1)配成p−12对,每对乘积为n,则np−12≡(p−1)!≡−1(modp)。最后的等号是威尔逊定理。

设a满足ω=a2−n不是二次剩余(即x2≡ω(modp)无解)

定理:x≡(a+ω√)p+12是方程x2≡n(modp)的解。

证明: (a+ω√)p=ap+ωp−12ω√=a−ω√,前面的等号用了二项式定理和(pi)≡0(modp),后面的等号用了费马小定理和ω不是二次剩余。

(a+ω√)p+1≡(a+ω√)p(a+ω√)≡(a−ω√)(a+ω√)≡a2−ω≡n

具体实现的时候,a的选择可以随机,因为大约有一半非二次剩余,剩下的只有快速幂而已。

根据以上叉姐博客里的结论可以得出也就是通俗的讲随机(0-p-1)选取一个随机数满足x^2=-1(mod p)无解也就是a是素数p的二次非剩余即成立。对二次域(a+sqrt(w))^(p+1)%p即为x0.

x0求出后在通过对进行欧几里德辗转相除运算,记每次的余数为,当满足条件时停止运算,此时的就是x,这样就得到了x,那么y可以通过

具体见程序注释。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}
ll Mulmod(ll a,ll b,ll n)
{
    ll  exp=a%n,res=0;
    while(b)
    {
        if(b&1)
        {
            res += exp;
            if(res>n) res -= n;
        }
        exp<<=1,b>>=1;
        if(exp>n) exp -= n;
    }
    return res;
}
ll exp_mod(ll a,ll b,ll c)
{
    ll k=1;
    while(b)
    {
        if(b&1) k=Mulmod(k,a,c);
        a=Mulmod(a,a,c),b>>=1;
    }
    return k;
}
bool Miller_Rabbin(ll n, ll times)
{
    if(n==2) return 1;
    if(n<2||!(n&1)) return 0;
    ll a,u=n-1,x,y;
    int t=0;
    while(u%2==0) t++,u/=2;
    srand(100);
    for(int i=0; i<times; i++)
    {
        a=rand()%(n-1)+1,x=exp_mod(a,u,n);
        for(int j=0; j<t; j++)
        {
            y=Mulmod(x,x,n);
            if(y==1&&x!=1&&x!=n-1) return false; //must not
            x=y;
        }
        if(y!=1) return false;
    }
    return true;
}
ll Pollard_Rho(ll n,ll c)
{
    ll x,y,d,i=1,k=2;
    y=x=rand()%(n-1)+1;
    while(1)
    {
        i++;
        x=(Mulmod(x,x,n)+c)%n,d=gcd((x-y+n)%n,n);
        if(d>1&&d<n) return d;
        if(x==y) return n;
        if(i==k) k<<=1,y=x;
    }
}
ll factor[100],tol,col,fac[100][2];
void Find_factor(ll n,ll c)    //大数分解 fac[i][0]存素因子 fac[i][1]存幂
{
    if(n==1) return;
    if(Miller_Rabbin(n,10))
    {
        factor[tol++]=n;
        return;
    }
    ll p=n,k=c;
    while(p>=n) p = Pollard_Rho(p,c--);
    Find_factor(p,k);
    Find_factor(n/p,k);
}
void getfactor() //把素因子转化为fac素组
{
    sort(factor,factor+tol);
    memset(fac,0,sizeof(fac));
    col=0,fac[col][0]=factor[0],fac[col][1]++;
    for(int i=1; i<tol; i++)
        if(factor[i]!=factor[i-1]) fac[++col][0]=factor[i],fac[col][1]++;
        else fac[col][1]++;
    col++;
}
bool judge() //判断是否存在4k+3为奇次幂的
{
    for(int i=0; i<col; i++)
        if(fac[i][0]%4==3&&fac[i][1]&1)
        {
            puts("No Solution");
            return 1;
        }
    return 0;
}
ll sfac[100][2],num;//存的是每个素因子拆分后的x,y
struct qua //二次域的结构体
{
    ll a,b;
};
ll w;//根号下的w
qua mul_quamod(qua a,qua b,ll n)
{
    qua ans;
    ans.a=(Mulmod(a.a,b.a,n)+Mulmod(Mulmod(a.b,b.b,n),w,n))%n;
    ans.b=(Mulmod(a.a,b.b,n)+Mulmod(b.a,a.b,n));
    return ans;
}
qua exp_quamod(qua a,ll b,ll n) //二次域的高次幂取模 原理跟整数的一样
{
    qua ans;
    ans.a=1,ans.b=0;
    while(b)
    {
        if(b&1) ans=mul_quamod(ans,a,n);
        b>>=1,a=mul_quamod(a,a,n);
    }
    return ans;
}
void getsfac(ll p,ll &x,ll &y)//得到4k+1型素因子的x,y
{
    ll a,m,t=(ll)sqrt(p),x0,r;
    while(1)        //随机数求1-sqrt(p)内的一个符合素数p的非二次剩余 这里到根号p是防止a*a超long long
    {
        a=rand()%t,m=a*a+1;
        if((exp_mod(m,(p-1)>>1,p)+1)%p==0) break;
    }
    w=a*a+1;
    qua qu; //定义二次域求x0
    qu.a=a,qu.b=1;
    qu=exp_quamod(qu,(p+1)>>1,p);
    x0=qu.a,t=p,r=p%x0;     //辗转相处求x
    while(r>=(ll)sqrt(p))
        t=x0,x0=r,r=t%x0;
    x=r,y=(ll)sqrt(p-r*r);  //通过x求y
}
void solve() //针对每种不同的素因子求得出x^2+y^2=p
{
    num=0;
    for(int i=0; i<col; i++)
        for(int j=0; j<fac[i][1]; j++,num++)
            if(fac[i][0]==2) sfac[num][0]=sfac[num][1]=1;
            else if(fac[i][0]%4==1) getsfac(fac[i][0],sfac[num][0],sfac[num][1]);
            else if(fac[i][0]%4==3) sfac[num][0]=0,sfac[num][1]=fac[i][0],j++;
}
struct finans //定义最后的结果
{
    ll x,y;
    bool operator<(const finans &o)const
    {
        return x<o.x;
    }
} data;
set<finans>wans[2];
set<finans>::iterator it;
int f1,f2;
void getans() //用了两个set 滚动存放每个素因子通过费马平方和后心的结果 用set的目的是去重
{
    wans[0].clear(),wans[1].clear();
    f1=1,f2=0;
    data.x=sfac[0][0],data.y=sfac[0][1];
    if(data.x>data.y)swap(data.x,data.y);
    wans[f1].insert(data);
    for(int i=1; i<num; i++)
    {
        swap(f1,f2);
        wans[f1].clear();
        for(it=wans[f2].begin(); it!=wans[f2].end(); it++)
        {
            data.x=(ll)fabs(it->x*sfac[i][0]+it->y*sfac[i][1]),
                   data.y=(ll)fabs(it->x*sfac[i][1]-it->y*sfac[i][0]);
            if(data.x>data.y)swap(data.x,data.y);
            wans[f1].insert(data);
            data.x=(ll)fabs(it->x*sfac[i][0]-it->y*sfac[i][1]),
                   data.y=(ll)fabs(it->x*sfac[i][1]+it->y*sfac[i][0]);
            if(data.x>data.y)swap(data.x,data.y);
            wans[f1].insert(data);
        }
    }
}
int main()
{
    ll n;
    while(~scanf("%I64d",&n))
    {
        if(n==1)
        {
            puts("0 1");
            continue;
        }
        tol=0,Find_factor(n,120);
        getfactor();
        if(judge()) continue;
        solve();
        getans();
        for(it=wans[f1].begin(); it!=wans[f1].end(); it++)
            printf("%I64d %I64d\n",it->x,it->y);
    }
    return 0;
}

ap−12≡±1(modp)

时间: 2024-08-02 13:41:55

NOD 1171 x^2+y^2=N的相关文章

javascript有声调的汉字注音字典(兼容各浏览器)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> <head><meta content="text/html; ch

num-计算若干个浮点数的平均值,以‘Y’作为输入结束

问题描述 计算若干个浮点数的平均值,以'Y'作为输入结束 include int main(void){ float numsum=0avg; int count=0; while(1) { scanf(""%f""&num); if(num==Y)break; count++; sum=sum+num; } if(count!=0) avg=sum/count; printf(""平均值为:%f"",avg): re

【导入导出】compress 值为y对导入对象所占空间的影响

我们知道 compress 默认Y 将第一个区占原有表的大小加compress=n 就取消了这种压缩,没有数据的话,空间也不会占用那么大了. 下面两篇文章,将分别讲述 compress=Y/N 时 导入表对表空间的占用情况.yang@ORACL> create table bigtab as select * from all_objects; 表已创建. 已用时间:  00: 00: 08.79 yang@ORACL> insert into bigtab select * from big

y轴-RRDTool (JRobin)绘图时指定Y轴阀值

问题描述 RRDTool (JRobin)绘图时指定Y轴阀值 比如,设定Y轴的阀值,小于这个值的时候画出的area是绿色,大于这个值画出的area是红色,谢谢

struts1-target is null for setProperty&amp;amp;lt;null,&amp;amp;#39;y&amp;amp;#39;,******&amp;amp;gt;

问题描述 target is null for setProperty<null,'y',******> 我用的是struts1,不知道是哪里的错,大神们拜托了..............

基于文本替换的解释器:递归,如何构造递归函数,Y组合子

递归.哦,递归. 递归在计算机科学中的重要性不言而喻. 递归就像女人,即令人烦恼,又无法抛弃. 先上个例子,这个例子里的函数double输入一个非负整数$n$,输出$2n$. \[ {double} = \lambda n.({if} \; ({iszero} \; n) \; 0 \; (+ \; 2 \; ({double} \; (- \; n \; 1)))) \] 现在的问题是,这个递归函数在我们的语言里没法直接定义. 我说的直接定义是指像这个用let表达式: \[ ({let} \;

Excel如何制作双Y轴柱形线性图表

  实例描述:制作一个双Y轴柱形.线性销售图表,用柱形表示销售额,线性图表示增长率(图1). 1. 图表制作数据先行 图表离不开数据的支持.打开SigmaPlot 12.3汉化版,新建笔记簿,点击"工作表"中的"导入文件"按钮,将保存在Excel中的数据导入到"数据1*"中.若数据列过多不好分辨,可右击列标,选择"列标题",对每列的标题进行更改.也可以在"数据1*"手工录入数据. 2. 柱形图表轻松做 数据

excel表格x轴y轴如何调换

  excelx轴y轴调换步骤如下: 1.打开excel; 2.点中图表,右键,点击"选择数据" 3.点击"切换行列",完成

WPS怎么绘制不重叠的双Y轴柱状图表?

  想绘制双Y轴图表,总是重叠怎么办?六步解决!(仅适用于少量数据) 1.数据重排,两列数据位置错开;两行数据为一个项目,项目之间插入两空行. 2.做成柱状图 3.选中→图表中次Y轴数据→右击→数据系列格式: ①→坐标轴→次坐标轴 ②→选项→分类间隔:0 4.选中→主Y轴数据→右击→数据系列格式→选项→分类间隔:0 5.部分图表成图后,X轴文字显示异常,解决方法:右击→X轴文字→坐标轴格式→刻度→分类数(分类轴刻度线标签之间):4 6.成图 注意事项:如若次Y轴数据太小,难以选中,可以拉伸图表.