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,pa(p是质数)
才会有原根… 题目中说模数是奇数也说明了一点端倪… 因为
2a,a>2
没有原根… 是不是告诉我们要质因数分解再搞原根啊…
如果是非质数的话, 将模数p质因数分解.
那么我们将每一个 piri 作为一个方程的模数, 那么我们会得到质因子个数个同余方程. 原方程的解带到这些新的同余方程里肯定也是满足条件的. 由中国剩余定理我们可以得到这样模意义下的解, 原方程的解的个数等于各个方程的解的乘积.
质因数分解之后就是喜闻乐见的分类讨论了… 为什么不能像原来那样解? 我们先看一下最基础的情况… 即 (b,pa)==1 的时候, 我们是这么来解的. 先求出当前模数的原根g. 那么求出x的离散对数之后.
然后这就是喜闻乐见的ax + by = c的样子啦… 其实答案就是(a, p). 因为范围是模以内, 推一下就知道了…
但是如果是 pa 的话可能和b不互质, 这样BSGS就无法求出ind了… (也许扩展bsgs就可以了?)
那么分类讨论一下, 发现 pa∣b 的话, 此时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;
}