[BZOJ]2219 数论之神 离散对数 + 原根

2219: 数论之神

Time Limit: 3 Sec Memory Limit: 259 MB
Submit: 1052 Solved: 322
[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

题解

  丁神以前讲过非质数… 这道题算是加强版了… 话说当时全班考了的时候就我一个人A了嘿嘿…现在如果非质数的话可能就没有原根了. 只有 1,2,4,p,pap 才会有原根… 题目中说模数是奇数也说明了一点端倪… 因为 2aa>2 没有原根… 是不是告诉我们要质因数分解再搞原根啊…
 如果是非质数的话, 将模数p质因数分解.
 

p=p1r1p2r2p3r3...

  那么我们将每一个 piri 作为一个方程的模数, 那么我们会得到质因子个数个同余方程. 原方程的解带到这些新的同余方程里肯定也是满足条件的. 由中国剩余定理我们可以得到这样模意义下的解, 原方程的解的个数等于各个方程的解的乘积.
  质因数分解之后就是喜闻乐见的分类讨论了… 为什么不能像原来那样解? 我们先看一下最基础的情况… 即 (b,pa)==1 的时候, 我们是这么来解的. 先求出当前模数的原根g. 那么求出x的离散对数之后.
  
gindgxagindgb(mod p)

  
indgxaindgb(mod φ(p))

  然后这就是喜闻乐见的ax + by = c的样子啦… 其实答案就是(a, p). 因为范围是模以内, 推一下就知道了…
  但是如果是 pa 的话可能和b不互质, 这样BSGS就无法求出ind了… (也许扩展bsgs就可以了?)
  那么分类讨论一下, 发现 pab 的话, 此时b为0, 那推一下发现快速幂就行了.
  如果互质的话, 那么解法如上面推得那样.
  如果只是gcd > 1的话, 抽出b的p的因子 pc 并同余式同使除以 pc , 就可以使之互质转化为第二种情况, 然后答案乘回来即可, 详情见代码. (有点像放缩…
  hh发现网上别人第二种情况后面写的有误, hack了一波, po爷好像也是错的?
  

#include<bits/stdc++.h>
#define hash zyc_hash
using namespace std;
typedef long long lnt;
const int maxn = 100010;
const int inf  = 1 << 30;
int A, B, P, K, G, T, ans;
int pri[maxn];
lnt mpow(lnt a,lnt b, lnt p)
{
    lnt ret = 1;
    while (b)
    {
        if (b & 1) ret = ret * a % p;
        a = a * a % p, b >>= 1;
    }
    return ret;
}
int gcd(int a,int b){return (!b) ? a : gcd(b, a % b);}
int Get_G(int p)
{
    int tmp = p - 1, m = sqrt(tmp);
    int cnt = 0;
    for (int i = 2; i <= m; ++ i)
        if (tmp % i == 0)
        {
            while (tmp % i == 0) tmp /= i;
            pri[++ cnt]=i;
        }
    if (tmp > 1) pri[++ cnt] = tmp;
    for (int g = 2; g < p; ++ g){
        bool ok = 1;
        for (int i = 1; i <= cnt; ++ i)
            if (mpow(g, (p - 1) / pri[i], p) == 1)
                {   ok = false; break;  }
        if (ok) return g;
    }
    return 0;
}
const int S = 114281;
struct Hash
{
    int head[S], dest[S][2], last[S], etot;
    inline void init()
    {
        memset(head, 0, sizeof(head));
        etot = 0;
    }
    inline void add(int a, int b)
    {
        int key = a % S;
        for(int t = head[key]; t; t = last[t])
            if(dest[t][0] == a) 
                {
                    dest[t][1] = b;
                    return;
                }
        last[++ etot] = head[key];
        dest[etot][0] = a;
        dest[etot][1] = b;
        head[key] = etot;
    }
    inline int query(int a)
    {
        int key = a % S;
        for(int t = head[key]; t; t = last[t])
            if(dest[t][0] == a) return dest[t][1];
        return -1;
    }
}hash;
int BSGS(int a, int b, int p)
{
    hash.init();
    int m = ceil(sqrt(p));
    lnt am = 1, mul = 1, val = b % p;
    hash.add(val, 0);
    for (int i = 1; i <= m; ++ i)
    {
        mul = mul * a % p;
        val = mul * b % p;
        hash.add(val, i);
    }
    am = mul, mul = 1;
    for (int i = 1, j; i <= m; ++ i)
    {
        mul = mul * am % p;
        if (~ (j = hash.query(mul))) return i * m - j;
    }
    return -1;
}
int solve(int p,int t)
{
    int pr = mpow(p,t, inf), b = B % pr;
    if (!b) return mpow(p, t - ((t-1) / A + 1), inf);
    int c = 0;
    while (b % p ==0) b /= p, c ++;
    if (c % A) return 0;

    int tmp = c / A;
    pr = mpow(p, t - c, inf);
    int phi = pr - pr / p;
    G = Get_G(p);
    int ind_gb = BSGS(G, b, pr);
    int d = gcd(A, phi);
    if (ind_gb % d) return 0; 
    return d * mpow(p, (A - 1) * tmp, inf);
}
int main()
{
    scanf("%d", &T);
    while(T --)
    {
        scanf("%d%d%d", &A, &B, &K);
        P = 2 * K + 1, K = P;
        int m = sqrt(P), c;
        ans = 1;
        for(int i = 2; i <= m && ans; ++ i)
        {
            if (K%i == 0)
            {
                c = 0;
                while (K%i == 0) K /= i, c ++;
                ans *= solve(i, c);
            }
        }
        if(ans && K > 1) ans *= solve(K, 1);
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值