【BZOJ 2219】【超详细题解】数论之神

2219: 数论之神

Time Limit: 3 Sec Memory Limit: 259 MB
Submit: 365 Solved: 33
[Submit][Status][Discuss]
Description

在ACM_DIY群中,有一位叫做“傻崽”的同学由于在数论方面造诣很高,被称为数轮之神!对于任何数论问题,他都能瞬间秒杀!一天他在群里面问了一个神题: 对于给定的3个非负整数 A,B,K 求出满足 (1) X^A = B(mod 2*K + 1) (2) X 在范围[0, 2K] 内的X的个数!自然数论之神是可以瞬间秒杀此题的,那么你呢?

Input

第一行有一个正整数T,表示接下来的数据的组数( T <= 1000) 之后对于每组数据,给出了3个整数A,B,K (1 <= A, B <= 10^9, 1 <= K <= 5 * 10^8)

Output

输出一行,表示答案

Sample Input

3

213 46290770 80175784

3 46290770 80175784

3333 46290770 80175784

Sample Output

27

27

297
HINT

新加数组一组–2015.02.27

Source

数论 鸣谢 AekdyCoin

中国剩余定理+阶+原根+指标+扩展欧几里得~

首先说说概念:

1.【阶】
满足 ax1(modp) 的最小的 x a 关于 p 的阶。
记作 δ(a)=x

2.【原根】
δ(g)=φ(p) ,则称 g p 的原根。

(1)只有 1,2,4,pk,2pk p 是奇素数)有原根。

(2) p 的原根个数为 φ(φ(p))
(证明见这里)

(3) p>1 , φ(p) 所有不同的因数为 p1,p2,,pk (g,m)=1 ,则 g 是模 p 的原根的充要条件是: gpi1(modp) 对于所有的 pi 都不成立
(这也是原根的求法)

3.【指标】
gra(modp) 成立,则称 r 是以 g 为底的 a 对模 m 的一个指标。
记作 r=inda

(1) ab(modp)indaindb(modφ(p))

(2) ind(ab)inda+indb(modφ(p))

(3) ind(an)ninda(modφ(p))

(4)指标的求法:
先求出 p 的原根 g ,然后用BSGS求出 gab(modp) 中的 a
(BSGS详见这里)

接下来说这道题。。

求方程 xAB(modP) 的解的个数。

P=pa11pa22pakk ,那么原方程的解的个数就是 xAB(modpaii) 的解的个数的乘积。

为什么呢?

对于每一个 xAB(modpaii) 方程我们从中选出一个解 xi

可以求出 xi mod paii 的解为 wi

设原方程的解为 X ,则 Xwi(modpaii)

于是形成了 k 个同余方程,根据中国剩余定理,一组同余方程对应一个 X 的解。

那么一共有 xAB(modpaii) 的解的个数的乘积个选法,就有对应的这么多的解。

于是问题变成了求 xAB(modpa) 的解的个数。

分三种情况讨论:

1. (pa,B)=B

此时原方程变成了

xA0(modpa)

x=pt ,那么要求 tAa
那么最小的 t=a1A+1
此方程的解的个数为

pa(a1A+1)

2. (pa,B)>1

B=pcntb ,原方程变为

xApcntb(modpa)

(1)如果 cnt mod A0 ,此方程无解。
(2)否则把方程转化为

(xpcntA)Ab(modpacnt)

由于此时 (pacnt,b)=1 ,方程转化成了第三种情况~

仅仅这样做是不够的!
在原式中 x 的取值范围是 [0,pa) ,那么 xpcntA 的取值范围就是 [0,pacntA)

可是在后一个式子中 xpcntA 的取值范围变成了 [0,pacnt) !!

因此我们需要在结果之后乘上 pcntcntA

3. (pa,B)=1

此时的方程是

xAB(modpa)

我们利用前面说的指标的性质可以将此方程转化为
AindxindB(modφ(pa))

利用BSGS求出 indB ,又因为 indx 对应着一个 x ,那么方程就变成了
axb(modp)

原方程解的个数对应上面那个方程的解的个数!!
(1)如果 b mod gcd(a,p)0 则无解;
(2)否则解的个数为 gcd(a,p)

吼吼,第一次用Mathjax和markdown写博客~

#include 
#include 
#include 
#include 
#include 
#include 
#define inf 1e17
#define LL long long
#include 
using namespace std;
map mp;
struct data
{
    LL p,c,pc;
}a[100005];
int num,cnt;
LL f[100005];
void Chai(int x)
{
    num=0;
    for (int i=2;i<sqrt(x+0.5);i++)
        if (x%i==0)
        {
            a[++num].p=i;
            a[num].c=0,a[num].pc=1;
            while (x%i==0)
                x/=i,a[num].c++,a[num].pc*=i;
            if (x==1) break;
        }
    if (x!=1)
        a[++num].p=x,a[num].pc=x,a[num].c=1;
}
LL Pow(LL x,LL n,LL mod)
{
    LL ans=1,b=x;
    while (n)
    {
        if (n&1)
            ans=ans*b%mod;
        b=b*b%mod;
        n>>=1LL;
    }
    return ans;
}
LL GetPrimitiveRoot(LL p,LL phi)
{
    int c=0;
    for (int i=2;i*i<=phi;i++)
        if (phi%i==0)
            f[++c]=i,f[++c]=phi/i;
    for (int g=2;;g++)
    {
        int j;
        for (j=1;j<=c;j++)
            if (Pow(g,f[j],p)==1) break;
        if (j==c+1) return g;
    }
    return 0;
}
void exgcd(LL a,LL b,LL &d,LL &x,LL &y)
{
    if (!b)
    {
        d=a,x=1,y=0;
        return;
    }
    exgcd(b,a%b,d,y,x);
    y-=x*(a/b);
}
LL BSGS(LL A,LL B,LL C)
{
    int m=ceil(sqrt(C+0.5));
    mp.clear();
    LL now=1;
    for (int i=1;i<=m;i++)
    {
        now=now*A%C;
        if (!mp[now]) mp[now]=i;
    }
    mp[1]=0;
    A=Pow(A,m,C);
    now=1LL;
    for (int i=0;i<=m;i++)
    {
        LL d,x,y;
        exgcd(now,C,d,x,y);
        x=(x*B%C+C)%C;
        if (mp.count(x)) return i*m+mp[x];
        now=now*A%C;
    }
    return 0;
}
LL Gcd(LL a,LL b)
{
    if (!b) return a;
    return Gcd(b,a%b);
}
LL Solve(LL A,LL B,LL k)
{
    LL phi=a[k].pc-a[k].pc/a[k].p,g=GetPrimitiveRoot(a[k].pc,phi);
    LL ind=BSGS(g,B,a[k].pc);
    LL ans=Gcd(phi,A);
    if (ind%ans) return 0;
    return ans*Pow(a[k].p,cnt-cnt/A,inf);
}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        LL A,B,k;
        scanf("%lld%lld%lld",&A,&B,&k);
        LL p=2*k+1;
        Chai(p);
        LL ans=1;
        for (int i=1;i<=num;i++)
        {
            if (!ans) break;
            if (B%a[i].pc==0)
                ans=ans*Pow(a[i].p,a[i].c-(a[i].c-1)/A-1,inf);
            else
            {
                int b=B;
                cnt=0;
                while ((b%a[i].p)==0)
                    b/=a[i].p,
                    a[i].pc/=a[i].p,
                    a[i].c--,cnt++;
                if (cnt%A) ans=0;
                else ans=ans*Solve(A,b,i);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

这里写图片描述

感悟:

1.WA是因为求原根的地方我没有对 p 取模

2.分类讨论+原根和指标的应用~

3.UPD:
①a关于p的阶x为什么一定是 φ(p) 的因数?

假设 p=kx+r r0 r<x ,那么 axar1(modp) ,与阶的定义(最小的模 p 1 的数)矛盾

p 与他的原根 g 一定互质吗?

一定的。
原根与欧拉定理 aφ(p)1(modp) 是密不可分的,欧拉定理规定 (a,p)=1

如果 (a,p)=k k>1 ,那么 (aφ(p),p)=(p,aφ(p)%p)k
显然不符合欧拉定理。

③为什么 p 的原根一定是 pa 的原根??

你可能感兴趣的:(~Mathematic~)