数学练习记
由于这个人觉得自己数学太差,他决定把紫书和蓝书的数学部分先刷一刷,再买本具体数学啃一啃,再把组合数学啃一啃,再把 PE 刷一刷。
这个人觉得上面紫书两个字之后的那些话可以不用信了
【UVA11582】斐波那契数列取模找循环节问题,考虑数列中两个相邻元素什么时候和 $f[0]$ 与 $f[1]$ 一样,就是 $0$ 后面什么时候重新跟一个 $1$,最坏情况就是 $0$ 后面跟一个 $2$,走了一段长度为 $n$ 的序列,后面跟了一个 $3$ 再走了一段长度为 $n$ 的序列...所以最多 $n^2$ 长度会出现循环,求出周期后再对 $a^b$ 进行快速幂即可。
#include <bits/stdc++.h> #define ull unsigned long long int qp(int a, ull b, int MOD) { int ans = 1; while (b) { if (b & 1) ans = ans * a % MOD; a = a * a % MOD; b >>= 1; } return ans % MOD; } const int N = 1e6 + 7; int f[N]; int main() { int T; scanf("%d", &T); while (T--) { ull a, b; int n; scanf("%llu%llu%d", &a, &b, &n); f[0] = 0; f[1] = 1 % n; int t = 0; for (int i = 2; ; i++) { f[i] = (f[i - 1] + f[i - 2]) % n; if (f[i] == f[1] && f[i - 1] == f[0]) { t = i - 1; break; } } int aa = a % t; printf("%d\n", f[qp(aa, b, t)]); } return 0; }
【UVA12169】可以得到 $x_3 = a^2 x_{1} + (a + 1)b \pmod {10001}$,数列长度只有 $200$(刚开始以为是 $10000$。。。),那么可以枚举 $a$,用扩展欧几里得得到 $b$,然后在 $O(T)$ 的时间check一下这个数列是否符合条件,然后就做完了。
#include <bits/stdc++.h> #define ll long long const int MOD = 10001; const int N = 220; ll f[N], in[N]; void exgcd(ll a, ll b, ll &d, ll &x, ll &y) { if (!b) { d = a, x = 1, y = 0; } else { exgcd(b, a % b, d, y, x); y -= x * (a / b); } } int main() { int n; scanf("%d", &n); for (int i = 1; i <= 2 * n; i += 2) scanf("%lld", in + i); f[1] = in[1]; for (int a = 0; a <= 10000; a++) { ll c = in[3] - a * a * in[1]; ll d, b, z; exgcd(a + 1, 10001, d, b, z); if (c % d) continue; b *= c / d; bool flag = 1; for (int i = 2; i <= 2 * n; i++) { f[i] = f[i - 1] * a + b; f[i] %= MOD; if (i & 1) { if (f[i] != in[i]) { flag = 0; break; } } } if (!flag) continue; for (int i = 2; i <= 2 * n; i += 2) printf("%lld\n", f[i]); break; } }
【UVA10375】求组合数,就是对分子分母都写成质数的形式,然后记录一下指数即可。
#include <bits/stdc++.h> const int N = 10007; int prime[N], zhi[N]; bool vis[N]; void init() { for (int i = 2; i < N; i++) { if (!vis[i]) prime[++prime[0]] = i; for (int j = 1; j <= prime[0] && i * prime[j] < N; j++) { vis[i * prime[j]] = 1; if (i % prime[j] == 0) break; } } } void add(int n, int i, int v) { int p = prime[i]; while (p <= n) { zhi[i] += v * n / p; if (n < 1LL * p * prime[i]) break; p *= prime[i]; } } void add(int n, int v) { for (int i = 1; i <= prime[0]; i++) add(n, i, v); } int main() { init(); int p, q, r, s; while (~scanf("%d%d%d%d", &p, &q, &r, &s)) { for (int i = 1; i <= prime[0]; i++) zhi[i] = 0; add(p, 1); add(q, -1); add(p - q, -1); add(r, -1); add(s, 1); add(r - s, 1); double ans = 1; for (int i = 1; i <= prime[0]; i++) ans *= pow(prime[i], zhi[i]); printf("%.5f\n", ans); } return 0; }
【UVA10791】把数唯一分解之后,然后每个数都单独取出来加上就行,只有一种质因子时加上1。
#include <bits/stdc++.h> const int N = 22; int p[N]; int main() { int n; int kase = 0; while (~scanf("%d", &n), n) { printf("Case %d: ", ++kase); if (n == 1) { puts("2"); continue; } int k = sqrt(n + 0.5); p[0] = 0; for (int i = 2; 1LL * i * i <= n; i++) { if (n % i == 0) { p[++p[0]] = 1; while (n % i == 0) p[p[0]] *= i, n /= i; } } if (n != 1) p[++p[0]] = n; if (p[0] == 1) { printf("%lld\n", 1LL * p[1] + 1LL); } else { int ans = 0; for (int i = 1; i <= p[0]; i++) ans += p[i]; printf("%d\n", ans); } } return 0; }
【UVA12716】设 $c = a - b$,因为 $gcd(a, b) \leq c \leq a$ xor $b$,那么当 $gcd(a, b) = a$ xor $b$ 时,$gcd(a, b) = a$ xor $b = c$,枚举 $a$、$c$,预处理一个 $sum[i]$ 表示 $i$ 为较大数时有多少较小数符合。做一下前缀和即可。复杂度 $O(nlogn)$。
#include <bits/stdc++.h> const int N = 3e7 + 7; int sum[N]; int main() { for (int i = 1; i < N; i++) for (int j = i + i; j < N; j += i) if ((j ^ (j - i)) == i) sum[j]++; for (int i = 1; i < N; i++) sum[i] += sum[i - 1]; int T; scanf("%d", &T); for (int kase = 1; kase <= T; kase++) { int n; scanf("%d", &n); printf("Case %d: %d\n", kase, sum[n]); } return 0; }
【UVA1635】可以发现,就是一个杨辉三角,第 $i$ 的系数为 $C_{n-1}^{i-1}$,由定义可得递推式 $C_{n}^{i} = \dfrac{n - i + 1}{i} C_{n}^{i - 1}$,把递推式改成质因子次数的递推即可。
#include <cstdio> #include <algorithm> #include <cstring> #include <vector> int n, m; const int M = 32000; int prime[M]; bool vis[M]; void init() { for (int i = 2; i < M; i++) { if (!vis[i]) prime[++prime[0]] = i; for (int j = 1; j <= prime[0] && i * prime[j] < M; j++) { vis[i * prime[j]] = 1; if (i % prime[j] == 0) break; } } } const int N = 1e5 + 7; int who[20], zhi[20], cnt; int ans[N][20]; void get(int n) { memset(who, 0, sizeof who); memset(zhi, 0, sizeof zhi); cnt = 0; for (int i = 1; i <= prime[0] && prime[i] <= n; i++) { if (n % prime[i]) continue; who[++cnt] = prime[i]; while (n % prime[i] == 0) n /= prime[i], zhi[cnt]++; if (n == 1) break; } if (n != 1) { who[++cnt] = n; zhi[cnt]++; } } int main() { init(); while (~scanf("%d%d", &n, &m)) { get(m); for (int i = 1; i < n; i++) { for (int j = 1; j <= cnt; j++) ans[i][j] = ans[i - 1][j]; int x = n - i; for (int j = 1; j <= cnt; j++) { while (x % who[j] == 0) ans[i][j]++, x /= who[j]; } x = i; for (int j = 1; j <= cnt; j++) { while (x % who[j] == 0) ans[i][j]--, x /= who[j]; } } std::vector<int> res; for (int i = 0; i < n; i++) { bool flag = 1; for (int j = 1; j <= cnt; j++) flag &= (ans[i][j] >= zhi[j]); if (flag) res.push_back(i + 1); } std::sort(res.begin(), res.end()); printf("%d\n", (int)res.size()); if (res.size()) { printf("%d", res[0]); for (int i = 1; i < res.size(); i++) printf(" %d", res[i]); } puts(""); } return 0; }
【UVA10820】欧拉函数前缀和。
#include <bits/stdc++.h> const int N = 50007; int p[N], tol, phi[N]; bool vis[N]; void init() { for (int i = 2; i < N; i++) { if (!vis[i]) { p[++tol] = i; phi[i] = i - 1; } for (int j = 1; j <= tol && i * p[j] < N; j++) { vis[i * p[j]] = 1; if (i % p[j] == 0) { phi[i * p[j]] = phi[i] * p[j]; break; } phi[i * p[j]] = phi[i] * phi[p[j]]; } } for (int i = 2; i < N; i++) phi[i] += phi[i - 1]; } int main() { int n; init(); while (~scanf("%d", &n), n) printf("%d\n", phi[n] * 2 + 1); return 0; }
【UVA1262】对每一列求出能选的字母,那么总方案数就是每一列能选的字母数的乘积,然后判断 $k$ 是后继方案数的多少倍,那么每一列要选的字母就出来了。
#include <bits/stdc++.h> #define select sel const int N = 10; char s[2][N][N]; bool vis[2][N][26]; int select[N][N]; char str[N]; int main() { int T; scanf("%d", &T); while (T--) { int k; scanf("%d", &k); for (int i = 0; i < 2; i++) for (int j = 0; j < 6; j++) scanf("%s", s[i][j]); memset(vis, 0, sizeof(vis)); memset(select, 0, sizeof(select)); memset(str, 0, sizeof str); for (int j = 0; j < 5; j++) { for (int z = 0; z < 2; z++) for (int i = 0; i < 6; i++) vis[z][j][s[z][i][j] - 'A'] = 1; for (int i = 0; i < 26; i++) if (vis[0][j][i] && vis[1][j][i]) select[j][++select[j][0]] = i; // for (int i = 1; i <= select[j][0]; i++) // printf("%d ", select[j][i]); // puts(""); } int ans = 1; for (int i = 0; i < 5; i++) ans *= select[i][0]; if (k > ans) { puts("NO"); continue; } k--; for (int i = 0; i < 5; i++) { ans /= select[i][0]; int pos = k / ans; str[i] = (char)(select[i][pos + 1] + 'A'); k %= ans; } str[5] = 0; puts(str); } return 0; }