【BZOJ 4253】密码箱加强版

首先无视掉这题题面,您就全当机房的神犇在D蒟蒻我好了。
进入正题,首先这题的弱化版还是蛮好想的。设:

n=pk11pk22...pknn

对于 x2=1(modn) ,一定有
x2=1(modpk11)
x2=1(modpk22)

xn=1(modpknn)
而对于 x2=1(modpkkk) :
pk!=2 时, x 有以下取值
x=pkkk1(modpkkk) x=1(modpkkk)
pk=2 时, x 有另外两个取值(注意特判下 kk=1 的情况)
x=pkk1k1(modpkkk) x=pkk1k+1(modpkkk)
于是我们穷举 x 对于每个因子的取值,中国剩余定理合并即可。
但是。。。。
这题的n显然比较大,普通的 n 筛质因子已经满足不了需求,于是我学习了可以 n 筛质因子的pollard rho和配合使用的rabin miller算法。想要学习的可以百度一下或者看一下算导,讲的都听明白的,代码的话不嫌丑的话可以看一看我的。code:

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#define s 20
using namespace std;
long long n,x,y;
long long prime[51],tot,b[51],ret[51];
long long ansi[320001],totans;
long long mult(long long a,long long b,long long p)
{
    long long t;
    if (b==0) return 0;
    if (b==1) return a;
    t=mult(a,b/2,p);
    if (b%2==0) return (t+t)%p;
    else return ((t+t)%p+a)%p;
}
long long power(long long a,long long b,long long p)
{
    long long temp;
    if (b==0) return 1;
    if (b==1) return a;
    temp=power(a,b/2,p);
    if (b%2==0) return mult(temp,temp,p);
    else return mult(a,mult(temp,temp,p),p);
}
bool check(long long a,long long n,long long x,long long t)
{
     long long i,ret=power(a,x,n);
     long long last=ret;
     for (i=1;i<=t;++i)
       {
         ret=mult(ret,ret,n);
         if (ret==1&&last!=1&&last!=n-1) return false;
         last=ret;
       }
    if (ret!=1) return false;
    else return true;
}
bool is_prime(long long n)
{
    long long a,i,x,t=0;
    if (n<2) return false;
    if (n==2) return true;
    if (!(n&1)) return false;
    x=n-1;
    if (!(x&1)) {x>>=1; t++;}
    for (i=1;i<=s;++i)
      {
        a=rand()%(n-2)+2;
        if (!check(a,n,x,t)) return false;
      }
    return true;
}
long long _abs(long long x)
{
    if (x>0) return x;
    else return -x; 
}
long long gcd(long long a,long long b)
{
    if (a%b==0) return b;
    else return gcd(b,a%b);
}
long long pollard_rho(long long p)
{
    long long y,d,k,i,x,x0,c;
    c=(long long)(long long)(rand()*(long long)rand())%p;
    y=x=(long long)((long long)rand()*(long long)rand())%p;
    k=2; i=1;
    while (true)
      {
        i++;
        x=(mult(x,x,p)+c)%p;
        d=gcd(_abs(y-x),p);
        if (d!=1) return d;
        if (i==k)
          {y=x; k<<=1;}
      }
}
void make_list(long long n)
{
    long long i;
    long long x,minn;
    bool f,d;
    f=is_prime(n);
    if (f) 
      {
        d=false;
        for (i=1;i<=tot;++i)
          if (prime[i]%n==0)
            {prime[i]*=n; d=true;}
        if (!d) prime[++tot]=n;   
      }
    else
      {
        minn=pollard_rho(n);
        while (minn==n) minn=pollard_rho(n);
        make_list(minn);
        make_list(n/minn);
      }
}
void exgcd(long long a,long long b,long long p)
{
    long long t;
    if (a%b==0)
      {
        x=0;
        y=1;
      }
    else
      {
        exgcd(b,a%b,p);
        t=x%p;
        x=y%p;
        y=(t-mult((a/b),y,p)+p)%p;
      }
}
void make_ret()
{
    long long i,now,t;
    for (i=1;i<=tot;++i)
      {
        now=n/prime[i];
        now=now%prime[i];
        exgcd(now,prime[i],prime[i]); 
        x=(x%prime[i]+prime[i])%prime[i];
        t=n/prime[i];
        ret[i]=mult(x,t,n);  
      }
}
void solve()
{
    long long i,now,ans=0,t;
    for (i=1;i<=tot;++i)
      {
        x=mult(ret[i],b[i],n); 
        ans=(ans+x)%n;  
      }
    ansi[++totans]=ans;
}
void work(long long i)
{
    if (i==tot+1)
      {
        solve();
        return;
      }
    if (prime[i]==2)
      {b[i]=1; work(i+1);}
    else
      {
        b[i]=prime[i]-1; work(i+1);
        b[i]=1; work(i+1);
        if (prime[i]%2==0&&prime[i]>4)
          {
            b[i]=prime[i]/2+1;
            work(i+1);
            b[i]=prime[i]/2-1;
            work(i+1);
          }
      }
}
int main()
{
    long long i,size;
    scanf("%lld",&n);
    if (n==1) printf("None\n");
    else
      {
        make_list(n);
        make_ret();
        work(1);
        sort(ansi+1,ansi+totans+1);
        size=unique(ansi+1,ansi+totans+1)-ansi-1;
        for (i=1;i<=size-1;++i)
          printf("%lld ",ansi[i]);
        printf("%lld\n",ansi[size]);
      }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值