【AHOI2007】【BZOJ4253】密码箱 加强版

Description
由于BZOJ的水题已经被Lcomyn大爷刷完了,所以机房的蒟蒻们决定再出一道水题给lcomyn刷。
正巧蒟蒻们最近做了AHOI2007密码箱,觉得这题实在太水了。稍微加强一下下依然还是很水的,所以决定把数据范围开大一点送给Lcomyn切。
求方程x^2=1(mod n)的解,x为小于n的非负整数。

Input
一行一个数n。

Output
如果方程无解输出None,否则就按从小到大的顺序输出所有解,两个数之间用空格隔开,行末无多余空格。

Sample Input
5
Sample Output
1 4
HINT

1<=n<=10^18

Source

By TA
题目跟原题一样数据范围扩大了
是为了让用Pollard_Rho做因数分解用Miller_Rabin判素数所以开打了范围
特地去学了一遍
跟着Lcomyn大爷学了姿势\

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#define LL long long
#define MAXN 320010
#define Abs(x)  ((x)>0?(x):-(x))
#define RAND (LL)((LL)rand()*(LL)rand())%p
using namespace std;
const int TEST_NUM=20;
LL n,x,y;
LL fac[64],top,b[64],ret[64];
LL ans[MAXN],num;
LL Mult(LL x,LL n,LL P)
{
    LL ret=0;
    for (LL i=n;i;i>>=1,x=(x<<1)%P)
        if (i&1)    ret+=x%P,ret%=P;ret=(ret+P)%P;
    return (ret+P)%P;
}
LL Pow(LL x,LL n,LL P)
{
    LL ret=1;
    for (LL i=n;i;i>>=1,x=Mult(x,x,P))
        if (i&1)    ret=Mult(ret,x,P);
    return (ret+P)%P;
}
bool check(LL a,LL n,LL x,LL t)
{
    LL ret=Pow(a,x,n),last=ret;
    for (LL i=1;i<=t;i++)
    {
        ret=Mult(ret,ret,n);
        if (ret==1&&last!=1&&last!=n-1) return 0;
        last=ret;
    }
    return (ret==1);
}
bool Miller_rabin(LL n)
{
    LL x=n-1,RANDNUM,t=0;
    if (n<=2)   return (n==2);
    if (!(n&1)) return 0;
    if (!(x&1)) x>>=1,t++;
    for (int i=1;i<=TEST_NUM;i++)
    {
        RANDNUM=rand()%(n-2)+2;
        if (!check(RANDNUM,n,x,t))  return 0;
    }
    return 1;
}
LL gcd(LL a,LL b)
{
    return !b?a:gcd(b,a%b);
}
LL Pollard_rho(LL p)
{
    LL i=1,k=2,x,y,c,ret;
    c=RAND;y=x=RAND;
    while (1)
    {
        i++;x=(Mult(x,x,p)+c)%p;ret=gcd(Abs(y-x),p);
        if (ret!=1) return ret;
        if (i==k)   y=x,k<<=1;
    }
}
void get_fac(LL n)
{
    LL i,minn;
    bool flag=Miller_rabin(n),b;
    if (flag)
    {
        b=0;
        for (i=1;i<=top;i++)
            if (fac[i]%n==0)    fac[i]*=n,b=1;
        if (!b) fac[++top]=n;
    }
    else
    {
//      cout<<n<<' ';
        minn=Pollard_rho(n);//cout<<minn<<endl;
        while (minn==n) minn=Pollard_rho(n);//,cout<<minn<<endl;
        get_fac(minn);get_fac(n/minn);
    }
}
void ex_gcd(LL a,LL b,LL p)
{
    LL tmp;
    if (a%b==0) x=0,y=1;
    else    ex_gcd(b,a%b,p),tmp=x%p,x=y%p,y=(tmp-Mult(a/b,y,p)+p)%p;
}
void make_ret()
{
    for (LL tmp,now,i=1;i<=top;i++) now=n/fac[i],now%=fac[i],ex_gcd(now,fac[i],fac[i]),x=(x%fac[i]+fac[i])%fac[i],tmp=n/fac[i],ret[i]=Mult(x,tmp,n);
}
void solve()
{
    LL new_ans=0;
    for (LL i=1;i<=top;i++) x=Mult(ret[i],b[i],n),new_ans=(new_ans+x)%n;
    ans[++num]=new_ans;
}
void work(LL i)
{
    if (i==top+1)   {solve();return;}
    if (fac[i]==2)  b[i]=1,work(i+1);
    else
    {
        b[i]=fac[i]-1;work(i+1);
        b[i]=1;work(i+1);
        if (!(fac[i]&1)&&fac[i]>4)  b[i]=fac[i]/2+1,work(i+1),b[i]=fac[i]/2-1,work(i+1);
    }
}
int main()
{
    cin>>n;
    if (n==1)   {puts("None");return 0;}
    get_fac(n);make_ret();work(1);
    sort(ans+1,ans+num+1);int size=unique(ans+1,ans+num+1)-ans-1;
    for (int i=1;i<size;i++)    printf("%lld ",ans[i]);printf("%lld\n",ans[size]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值