BZOJ 3384 上帝与集合的正确用法
上帝与集合的正确用法
【问题描述】
【输入格式】
第一行一个T,接下来T行,每行一个正整数p,代表你需要取模的值。
【输出格式】
T行,每行一个正整数,为答案对p取模后的值。
【样例输入】
3
2
3
6
【样例输出】
0
1
4
【数据范围】
对于100%的数据,T<=1000,p<=10^7
题解:
①->②:把模数 p 拆成 2kq 的形式,其中 q 是奇数
②->③:
将上式左右同除以2k
不会同余的蒟蒻只能这么推了
③->④:
此时 q 是奇数,必定与 2n 互质
则套用欧拉定理
考虑一个数的 phi 必定比它本身的值小
那么如此递归下去模数会变为 1,则返回 0
回溯得到答案
1 #include<cmath> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 int n; 9 inline void Scan(int &x) 10 { 11 char c; 12 bool o = false; 13 while(!isdigit(c = getchar())) o = (c != '-') ? o : true; 14 x = c - '0'; 15 while(isdigit(c = getchar())) x = x * 10 + c - '0'; 16 if(o) x = -x; 17 } 18 int Phi(int x) 19 { 20 int ans = x; 21 for(int i = 2; i * i <= x; ++i) 22 { 23 if(!(x % i)) 24 { 25 while(!(x % i)) x /= i; 26 ans /= i, ans *= (i - 1); 27 } 28 } 29 if(x ^ 1) ans /= x, ans *= (x - 1); 30 return ans; 31 } 32 int Pow(int x, int n, int mod) 33 { 34 int sum = 1; 35 while(n) 36 { 37 if(n & 1) sum = (long long) sum * x % mod; 38 x = (long long) x * x % mod; 39 n >>= 1; 40 } 41 return sum % mod; 42 } 43 int Work(int p) 44 { 45 if(p == 1) return 0; 46 int k = 0; 47 while(!(p & 1)) p >>= 1, ++k; 48 int phi = Phi(p); 49 int s = (Work(phi) - k) % phi; 50 if(s < 0) s += phi; 51 return Pow(2, s, p) << k; 52 } 53 int main() 54 { 55 Scan(n); 56 int p; 57 for(int i = 1; i <= n; ++i) 58 { 59 Scan(p); 60 printf("%d\n", Work(p)); 61 } 62 }