挑战程序设计竞赛 2.6 数学问题的解题窍门
数学问题的解题窍门
辗转相除法
AOJ 0005 输入2个数,输出它们的gcd和lcm
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int gcd(int a, int b) { 5 if (b == 0) return a; 6 return gcd(b, a % b); 7 } 8 9 int main() { 10 int a, b; 11 while (scanf("%d%d", &a, & b) != EOF) { 12 int p = gcd(a, b); 13 printf("%d %d\n", p, a / p * b); 14 } 15 }
POJ 2429 输入a和b的gcd和lcm(小于2^63),输出满足的a和b并且a+b要最小
难点在于大数的因式分解,详情见这篇
POJ 1930 给出一个无限循环小数,输出它的分数形式中分母最小的一种
假设后x位是循环节,乘以pow(10,x)减去原来的小数可以消去无限循环的部分。枚举循环节长度
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 using namespace std; 6 #define ll long long 7 8 ll gcd(ll x, ll y) { 9 if (y == 0) return x; 10 return gcd(y, x % y); 11 } 12 13 int main() { 14 string s; 15 while (cin >> s && s.size() > 1) { 16 ll ra = 0, rb = 0; 17 ll sum = 0; 18 for (int i = 2; i < s.size() - 3; i++) { 19 sum *= 10; 20 sum += s[i] - '0'; 21 } 22 for (int i = 2; i < s.size() - 3; i++) { 23 ll ts = 0; 24 for (int j = 2; j < i; j++) { 25 ts *= 10; 26 ts += s[j] - '0'; 27 } 28 ll a = sum - ts, 29 b = ((ll)pow(10, s.size() - 3 - i) - 1) * (ll)pow(10, i - 2); 30 ll c = gcd(a, b); 31 a /= c; 32 b /= c; 33 if (rb == 0 || b < rb) ra = a, rb = b; 34 } 35 cout << ra << "/" << rb << "\n"; 36 } 37 }
素数
AOJ 0009 输入一个数,输出不大于该数的质数的个数
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int n = 1e7; 5 int cnt, prime[n + 5], vis[n + 5], r[n + 5]; 6 7 int main() { 8 for (int i = 2; i <= n; i++) { 9 if (!vis[i]) prime[++cnt] = i; 10 for (int j = 1; j <= cnt && (long long)i * prime[j] <= n; j++) { 11 vis[i * prime[j]] = 1; 12 if (i % prime[j] == 0) break; 13 } 14 } 15 16 for (int i = 2; i <= n; i++) { 17 if (!vis[i]) 18 r[i] = r[i - 1] + 1; 19 else 20 r[i] = r[i - 1]; 21 } 22 23 int a; 24 while (cin >> a) cout << r[a] << "\n"; 25 26 }
POJ 3126 给出2个4位质数,每次改变一个数位,保证仍然是质数。问至少要改变几次
预处理质数建图,跑最短路。bfs也可以
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <queue> 6 #include <vector> 7 using namespace std; 8 9 #define P pair<int, int> 10 const int N = 1e5; 11 int cnt, prime[N + 5], vis[N + 5], r[N + 5]; 12 13 vector<int> edge[N + 5]; 14 int d[N + 5]; 15 16 int main() { 17 for (int i = 2; i <= N; i++) { 18 if (!vis[i]) prime[++cnt] = i; 19 for (int j = 1; j <= cnt && (long long)i * prime[j] <= N; j++) { 20 vis[i * prime[j]] = 1; 21 if (i % prime[j] == 0) break; 22 } 23 } 24 for (int i = 1000; i <= 9999; i++) { 25 if (!vis[i]) { 26 for (int j = 0; j < 4; j++) { 27 int t = i - i / (int)pow(10, j) % 10 * (int)pow(10, j); 28 for (int k = 0; k <= 9; k++) { 29 if (k == 0 && j == 3) continue; 30 int p = t + (int)pow(10, j) * k; 31 if (i == p || vis[p]) continue; 32 edge[i].push_back(p); 33 } 34 } 35 } 36 } 37 int t, s, e; 38 cin >> t; 39 while (t--) { 40 memset(d, 127, sizeof(d)); 41 cin >> s >> e; 42 priority_queue<P, vector<P>, greater<P> > que; 43 d[s] = 0; 44 que.push(P(0, s)); 45 while (!que.empty()) { 46 P p = que.top(); 47 que.pop(); 48 int v = p.second; 49 if (d[v] < p.first) continue; 50 for (int i = 0; i < edge[v].size(); i++) { 51 int q = edge[v][i]; 52 if (d[q] > d[v] + 1) { 53 d[q] = d[v] + 1; 54 que.push(P(d[q], q)); 55 } 56 } 57 } 58 if (d[e] == 0x7f7f7f7f) 59 puts("Impossible"); 60 else 61 cout << d[e] << "\n"; 62 } 63 }
POJ 3421 输入一个数(<2^20),构造一个依次整除的序列。问序列最长长度和该最长长度的种类数
质因数分解。序列相邻的数只差一个质数倍,因此最长长度是质因数分解的指数和,种类数可以用全排列除以重复次数(20!约为2e18,不会爆long long)
1 #include <cmath> 2 #include <cstdio> 3 #include <iostream> 4 using namespace std; 5 const int N = 1025; 6 7 int prime[N + 5], vis[N + 5], cnt; 8 9 int main() { 10 for (int i = 2; i <= N; i++) { 11 if (!vis[i]) prime[++cnt] = i; 12 for (int j = 1; j <= cnt && i * prime[j] <= N; j++) { 13 vis[i * prime[j]] = 1; 14 if (i % prime[j] == 0) break; 15 } 16 } 17 int a; 18 while (scanf("%d", &a) != EOF) { 19 int r = 0, num[30] = {0}, p = 0; 20 for (int i = 1; i <= cnt && a > 1; i++) { 21 if (a % prime[i] == 0) p++; 22 while (a % prime[i] == 0) { 23 a /= prime[i]; 24 r++; 25 num[p]++; 26 } 27 } 28 if (a > 1) r++; 29 long long mul = 1; 30 for (int i = 1; i <= r; i++) mul *= i; 31 for (int i = 1; i <= p; i++) { 32 for (int j = 1; j <= num[i]; j++) { 33 mul /= j; 34 } 35 } 36 printf("%d %lld\n", r, mul); 37 } 38 }
POJ 3292 定义一个4n+1的数域(对乘法封闭),称作H数。定义所谓的H质数和半H质数,前者是只有2个H数因子,后者是2个H质数的积。问给定范围内半H质数的个数
对欧拉筛做些修改即可,一遍筛质数一遍求半H质数
1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 5 const int N = 1e6 + 1; 6 int prime[N + 5], vis[N + 5], h[N + 5], semi[N + 5], sum[N + 5], cnt; 7 8 int main() { 9 for (int i = 5; i <= N; i += 4) { 10 if (!vis[i]) h[++cnt] = i; 11 for (int j = 1; j <= cnt && (long long)i * h[j] <= N; j++) { 12 vis[i * h[j]] = 1; 13 if (!vis[i]) semi[i * h[j]] = 1; 14 if (i % h[j] == 0) break; 15 } 16 } 17 for (int i = 1; i <= N + 1; i++) { 18 if (semi[i]) 19 sum[i] = sum[i - 1] + 1; 20 else 21 sum[i] = sum[i - 1]; 22 } 23 int a; 24 while (scanf("%d", &a) != EOF && a) printf("%d %d\n", a, sum[a]); 25 }
快速幂运算
POJ 3641 询问p是否是以a为基的伪素数
1 #include <cmath> 2 #include <cstdio> 3 #include <iostream> 4 using namespace std; 5 #define ll long long 6 const int N = 33000; 7 int prime[N + 1], vis[N + 1], cnt; 8 9 ll ksm(ll a, ll n, ll mod) { 10 ll r = 1; 11 while (n) { 12 if (n & 1) r = r * a % mod; 13 a = a * a % mod; 14 n >>= 1; 15 } 16 return r; 17 } 18 19 int main() { 20 for (int i = 2; i <= N; i++) { 21 if (!vis[i]) prime[++cnt] = i; 22 for (int j = 1; j <= cnt && i * prime[j] <= N; j++) { 23 vis[i * prime[j]] = 1; 24 if (i % prime[j] == 0) break; 25 } 26 } 27 ll p, a; 28 while (scanf("%lld%lld", &p, &a) != EOF && !(a == 0 && p == 0)) { 29 int f = 1; 30 for (int i = 1; i <= cnt && prime[i] < p; i++) { 31 if (p % prime[i] == 0) { 32 f = 0; 33 break; 34 } 35 } 36 if (f) { 37 cout << "no\n"; 38 continue; 39 } 40 if (ksm(a, p, p) == a) 41 cout << "yes\n"; 42 else 43 cout << "no\n"; 44 } 45 }
POJ 1995 按给定式子模拟
1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 #define ll long long 5 6 ll ksm(ll a, ll n, ll mod) { 7 int r = 1; 8 while (n) { 9 if (n & 1) r = r * a % mod; 10 a = a * a % mod; 11 n >>= 1; 12 } 13 return r; 14 } 15 16 int main() { 17 int t; 18 cin >> t; 19 while (t--) { 20 int p, n; 21 ll r = 0, a, b; 22 scanf("%d%d", &p, &n); 23 while (n--) { 24 scanf("%lld%lld", &a, &b); 25 r = (r + ksm(a, b, p)) % p; 26 } 27 printf("%lld\n", r); 28 } 29 }
END

浙公网安备 33010602011771号