《挑战程序设计竞赛》课后练习题解集——2.6 数学问题的解题窍门

数学问题的解题窍门

辗转相除法

 

AOJ 0005  输入2个数,输出它们的gcd和lcm

 1 #include 
 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 }
View Code

 

POJ 2429  输入a和b的gcd和lcm(小于2^63),输出满足的a和b并且a+b要最小

难点在于大数的因式分解,详情见这篇 

 

 POJ 1930  给出一个无限循环小数,输出它的分数形式中分母最小的一种

假设后x位是循环节,乘以pow(10,x)减去原来的小数可以消去无限循环的部分。枚举循环节长度

 1 #include 
 2 #include 
 3 #include 
 4 #include 
 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 }
View Code

 

素数

AOJ 0009  输入一个数,输出不大于该数的质数的个数

 1 #include 
 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 }
View Code

 

POJ 3126  给出2个4位质数,每次改变一个数位,保证仍然是质数。问至少要改变几次

预处理质数建图,跑最短路。bfs也可以 

 1 #include 
 2 #include 
 3 #include 
 4 #include 
 5 #include 
 6 #include 
 7 using namespace std;
 8 
 9 #define P pair
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, greater

> 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 }

View Code

 

POJ 3421  输入一个数(<2^20),构造一个依次整除的序列。问序列最长长度和该最长长度的种类数

质因数分解。序列相邻的数只差一个质数倍,因此最长长度是质因数分解的指数和,种类数可以用全排列除以重复次数(20!约为2e18,不会爆long long)

 1 #include 
 2 #include 
 3 #include 
 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 }
View Code

 

 POJ 3292  定义一个4n+1的数域(对乘法封闭),称作H数。定义所谓的H质数和半H质数,前者是只有2个H数因子,后者是2个H质数的积。问给定范围内半H质数的个数

对欧拉筛做些修改即可,一遍筛质数一遍求半H质数

 1 #include 
 2 #include 
 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 }
View Code

 

快速幂运算

POJ 3641  询问p是否是以a为基的伪素数

 1 #include 
 2 #include 
 3 #include 
 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 }
View Code

 

POJ 1995  按给定式子模拟

 1 #include 
 2 #include 
 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 }
View Code

 

END

你可能感兴趣的:(《挑战程序设计竞赛》课后练习题解集——2.6 数学问题的解题窍门)