數論啊。http://acm.hdu.edu.cn/showproblem.php?pid=4335
給定bpm,求有幾個n滿足條件。
證明見http://hi.baidu.com/aekdycoin/item/e493adc9a7c0870bad092fd9
題目給定b,p,M 問 0 <= n <= M 中有多少個數滿足 n^(n!) ≡ b (MOD p),並且可惡的是這裏沒有提到任何數的特殊性質,給定都只是限定在正整數且p>0。
有歐拉定理我們知道 n^(phi(p)) ≡ 1 (mod p) 但是這裏要求gcd(n, p) = 1,顯然題目並沒有這麼要的數據,那麼如果題目給定是滿足n,p互質的話,那麼我們就可以知道
n^(x) mod p 是有循環節的,這個循環節就是n^(phi(p)),如果n,p不互質的話,那麼我們可以證明這個循環節 T | phi(p),所以我們還是可以選擇phi(p)來作爲循環節,於是
下面我們就可以分四部分來進行計算了。
part_1:首先計算n! < phi(p) 頂多8個點,for循環直接暴力
part_2:如果還剩餘有區間的話,在計算 phi(p) <= n! < s!, s=min{x|x! mod phi(p)=0} // 此時的n!次方已經存在一個循環節,但是n還是變化的
part_3:接下來的區間就是n! % phi(p) = 0, 這說明其已經完全依賴於phi(p)的值了,所以只要考慮到n的循環性質了,直接統計n的一個循環節裏面出現次數就可以了
part_4:把餘下的n進行統計即可
#include <cstdlib> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define MAXN 100000 using namespace std; typedef unsigned long long int Int64; int p[MAXN+5], pm[10000], phi[MAXN+5], idx = -1, MOD, B; int Fac[MAXN+5]; Int64 M, ans; void GetPrime() { for (int i = 2; i <= MAXN; ++i) { if (!p[i]) { pm[++idx] = i; } for (int j = 0; pm[j]*i <= MAXN; ++j) { p[pm[j]*i] = 1; if (i % pm[j] == 0) { break; } } } /* printf("%d\n", idx); for (int i = 0; i <= 100; ++i) { printf("%d ", pm[i]); }*/ } void Eular() { phi[1] = 1; for (int i = 2; i <= MAXN; ++i) { // printf("phi[%d] = %d\n", i-1, phi[i-1]); // getchar(); if (!p[i]) { phi[i] = i - 1; continue; } for (int j = 0; pm[j]*pm[j] <= i; ++j) { if (i % pm[j] == 0) { if (i / pm[j] % pm[j] == 0) { phi[i] = pm[j] * phi[i/pm[j]]; } else { phi[i] = phi[pm[j]] * phi[i/pm[j]]; } break; } } } } bool _pow(Int64 a, Int64 b) { Int64 ret = 1; while (b) { if (b & 1) { ret *= a; ret %= MOD; } a *= a; a %= MOD; b >>= 1; } return ret == B; } void deal() { int LIM = -1, ptr; for (ptr = 0; ptr <= M; ++ptr) { // 第一段,小於phi[MOD]的一部分 if (!ptr) { Fac[ptr] = 1; } else { Fac[ptr] = Fac[ptr-1] * ptr; } if (Fac[ptr] >= phi[MOD]) { break; } if (_pow(ptr, Fac[ptr])) { ++ans; } } if (ptr > M) { return; } // 如果已經超過了M的話 for (int i = ptr; i <= M; ++i) { if (!i) { Fac[i] = 1 % phi[MOD]; } else { Fac[i] = (Fac[i-1] * i) % phi[MOD]; } if (Fac[i] == 0) { // 如果找到了這個點 LIM = i; break; } if (_pow(i, Fac[i]+phi[MOD])) { ++ans; } } if (LIM == -1) { return; } // 如果沒有找到一個是phi[MOD]倍數的點 int t = 0; if ((M - LIM+1) >= MOD) { // LIM 這個點是沒有計算的,所以長度爲M-LIM+1,這裏判定剩餘是否大於MOD for (int i = LIM; i < LIM+MOD; ++i) { if (_pow(i%MOD, phi[MOD])) { // 由於此時已經是phi[MOD]的倍數,所以直接把phi[MOD]這個指數代入 ++t; } } ans += (M - LIM+1) / (1LLU*MOD) * t; } int left = (M-LIM+1) % MOD; // 殘餘的餘數 for (int i = LIM; i < LIM+left; ++i) { if (_pow(i%MOD, phi[MOD])) { ++ans; } } } int main() { GetPrime(); Eular(); int T, ca = 0; scanf("%d", &T); while (T--) { ans = 0; scanf("%d %d %I64u", &B, &MOD, &M); if(M == 18446744073709551615LLU && MOD == 1 && B == 0) { // 由於b=0,p=1,所以可以直接得到M+1,由於溢出原因所以需要特判 printf("Case #%d: 18446744073709551616\n", ++ca); } else { deal(); printf("Case #%d: %I64u\n", ++ca, ans); } } return 0; }