欧拉计划 61~70
61
开幕雷击,其实我们可以利用 BFS 分层搜索,然后看能不能构成循环就行了。对于图中的节点,我们只用记录前两位和后两位,因为成环之后,每个数字都出现了 \(2\) 次(最高位和最低位),因此答案乘 \(101\) 即可,跑得非常快。
// cpp
#include<bits/stdc++.h>
using namespace std;
vector<int> edge[6][100];
void create() {
for (int n = 1; n; n ++ ) {
int p = n * (n + 1) / 2;
if (p >= 10000) break;
if (p / 100 == 0 || p % 100 == 0) continue;
if (p > 1000) edge[0][p / 100].push_back(p % 100);
}
for (int n = 1; n; n ++ ) {
int p = n * n;
if (p >= 10000) break;
if (p / 100 == 0 || p % 100 == 0) continue;
if (p > 1000) edge[1][p / 100].push_back(p % 100);
}
for (int n = 1; n; n ++ ) {
int p = n * (3 * n - 1) / 2;
if (p >= 10000) break;
if (p / 100 == 0 || p % 100 == 0) continue;
if (p > 1000) edge[2][p / 100].push_back(p % 100);
}
for (int n = 1; n; n ++ ) {
int p = n * (2 * n - 1);
if (p >= 10000) break;
if (p / 100 == 0 || p % 100 == 0) continue;
if (p > 1000) edge[3][p / 100].push_back(p % 100);
}
for (int n = 1; n; n ++ ) {
int p = n * (5 * n - 3) / 2;
if (p >= 10000) break;
if (p / 100 == 0 || p % 100 == 0) continue;
if (p > 1000) edge[4][p / 100].push_back(p % 100);
}
for (int n = 1; n; n ++ ) {
int p = n * (3 * n - 2);
if (p >= 10000) break;
if (p / 100 == 0 || p % 100 == 0) continue;
if (p > 1000) edge[5][p / 100].push_back(p % 100);
}
}
void search() {
int reflection[5] = {1, 2, 3, 4, 5};
do {
queue<pair<int, pair<int, int>>> q;
for (int start = 1; start < 100; start ++ ) {
for (auto p : edge[0][start]) {
q.push({start, {p, p}});
}
}
for (int i = 0; i < 5; i ++ ) {
queue<pair<int, pair<int, int>>> tmp;
while (q.size()) {
auto t = q.front(); q.pop();
for (auto nxt : edge[reflection[i]][t.second.first]) {
tmp.push({t.first, {nxt, t.second.second + nxt}});
}
}
swap(q, tmp);
}
while (q.size()) {
auto t = q.front(); q.pop();
if (t.first == t.second.first) cout << t.second.second * 101 << "\n";
}
} while (next_permutation(reflection, reflection + 5));
}
int main() {
create();
search();
return 0;
}
62
暴力出奇迹。
# python
dic = {''.join(sorted(str(pow(i, 3)))) : 0 for i in range(10000)}
for i in range(10000):
dic[''.join(sorted(str(pow(i, 3))))] += 1
for i in range(10000):
if dic[''.join(sorted(str(pow(i, 3))))] == 5:
print(pow(i, 3))
break
63
由于 \(len = [\lg x] + 1\),且 \(\lg x^n = n\lg x\),所以底数显然小于 \(10\),而 \(n \lg x \ge n - 1\) 告诉我们 \(x^{\frac{n}{n - 1}} \ge 10\),取 \(x = 9\),\(n\) 的上界为 \(21\),所以暴力算就行。
# python
ans = 0
for i in range(1, 22):
for j in range(1, 10):
if len(str(pow(j, i))) == i:
ans += 1
print(ans)
64
神秘模拟,我们考虑到对一个数 \(x\),\(\sqrt{x}\) 总是出现在它的上部,所以直接不管那一部分,记录分母和分子上的有理数,模拟倒数的过程,如果相同的分数出现两次则出现循环。
// cpp
#include<bits/stdc++.h>
using namespace std;
int continued_fraction(int x) {
int len = 0, fraction = 1, sqr = -sqrt(x), res = sqr;
if (res * res == x) return 0;
map<pair<int, int>, int> mp;
while (1) {
len ++ ;
int _fraction = x - res * res;
int d = __gcd(_fraction, fraction);
_fraction /= d, fraction /= d;
res = (-res * fraction % _fraction - sqr) % _fraction + sqr;
fraction = _fraction;
if (mp.count({res, fraction})) return len - mp[{res, fraction}];
mp[{res, fraction}] = len;
}
}
int main() {
int ans = 0;
for (int i = 1; i <= 10000; i ++ ) {
if (continued_fraction(i) & 1) {
ans ++ ;
}
}
cout << ans << "\n";
return 0;
}
65
连分数怎么这么坏啊,还是 python 好用。
// cpp
from fractions import *
e = [2, 1]
for i in range(98):
if i % 3 == 0: e.append((i // 3 + 1) * 2)
else: e.append(1)
e = e[::-1]
ans = Fraction(e[0])
e.pop(0)
for i in e:
ans = 1 / ans + i
print(sum(map(int, str(ans.numerator))))
66
考虑利用连分数求解佩尔方程。
定理1
\(sqrt{D}\) 的连分数分解满足
即循环节的最后一位恰好是第一位的两位。
基于该定理,我们可以较为简便的写出连分数展开。
定理2
记
当连分数展开的循环节长度为奇数时,佩尔方程有最小解:
当连分数展开的循环节长度为偶数时,佩尔方程有最小解:
利用该定理,我们可以在求解出连分数展开的循环节后,方便的求出最小解。
接下来我们着手连分数的展开。
考虑到每一级展开时连分数的式子满足:
因此其可提出一个常数:
考虑下一级解的形态:
不难看出
可以证明,\(c_i\) 在过程中始终是整数,因此我们可以写出一份代码。
# python
from fractions import *
from numpy import *
def solve(D):
b = 0
c = 1
sq = int(sqrt(D))
if sq * sq == D:
return []
lis = [int((sq + b) / c)]
while lis[-1] != lis[0] * 2:
b = lis[-1] * c - b
c = (D - b * b) // c
lis.append(int((sq + b) / c))
return lis
def pell_min(D):
lis = solve(D)
if len(lis) == 0:
return 0, 0
x, y = 0, 1
lis.pop(-1)
r = len(lis)
while len(lis) > 0:
x, y = y, x + lis[-1] * y
lis.pop(-1)
if r & 1:
return 2 * x * x - 1, 2 * x * y
else:
return x, y
ans = 0
maxD = 0
for D in range(1, 1001):
x, y = pell_min(D)
if ans < x:
ans = x
maxD = D
print(maxD, ans)
67
数字三角形,定义动态规划转移方程:
取最后一行的最大值即可。
// cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int a[N][N], dp[N][N];
int main() {
int n = 100;
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= i; j ++ ) {
cin >> a[i][j];
}
}
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= i; j ++ ) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + a[i][j];
}
}
int ans = 0;
for (int i = 1; i <= n; i ++ ) ans = max(ans, dp[n][i]);
cout << ans;
return 0;
}
68
因为只有十个数,我们直接对点标号,可以直接遍历所有排列求解。

// cpp
#include<bits/stdc++.h>
using namespace std;
string to_c(int t) {
if (t == 10) return "10";
string s = "";
s += char(t + '0');
return s;
}
string trans(int *a) {
string s = "";
for (int i = 0; i < 5; i ++ ) {
s += to_c(a[i * 2 + 1]);
s += to_c(a[i * 2]);
s += to_c(a[(i + 1) * 2 % 10]);
}
return s;
}
int main() {
int a[10];
iota(a, a + 10, 1);
string ans = "";
do {
if (a[0] + a[1] != a[3] + a[4]) continue;
if (a[2] + a[3] != a[5] + a[6]) continue;
if (a[4] + a[5] != a[7] + a[8]) continue;
if (a[6] + a[7] != a[9] + a[0]) continue;
if (a[8] + a[9] != a[1] + a[2]) continue;
if (a[1] > min(a[3], min(a[5], min(a[7], a[9])))) continue;
string s = trans(a);
if (s.length() == 16 && s > ans) ans = s;
} while(next_permutation(a, a + 10));
cout << ans << "\n";
return 0;
}
69
筛出欧拉函数即可。
// cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int primes[N], st[N], cnt;
int phi[N];
int main() {
phi[1] = 1;
for (int i = 2; i < N; i ++ ) {
if (!st[i]) primes[cnt ++ ] = i, phi[i] = i - 1;
for (int j = 0; i * primes[j] < N; j ++ ) {
st[i * primes[j]] = 1;
if (i % primes[j] == 0) {
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
int ans = 0;
double maxx = 0;
for (int i = 2; i <= int(1e6); i ++ ) {
if ((double)i / phi[i] > maxx) {
maxx = (double)i / phi[i];
ans = i;
}
}
cout << ans << " " << maxx << "\n";
return 0;
}
70
同样的方法,筛出之后暴力判断即可,比较好的一个剪枝是先判大小再判合不合法。
// cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
int primes[N], st[N], cnt;
int phi[N];
bool check(int x) {
string s = to_string(x), t = to_string(phi[x]);
sort(s.begin(), s.end()), sort(t.begin(), t.end());
return s == t;
}
int main() {
phi[1] = 1;
for (int i = 2; i < N; i ++ ) {
if (!st[i]) primes[cnt ++ ] = i, phi[i] = i - 1;
for (int j = 0; i * primes[j] < N; j ++ ) {
st[i * primes[j]] = 1;
if (i % primes[j] == 0) {
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
int ans = 0;
double maxx = 1e9;
for (int i = 2; i < int(1e7); i ++ ) {
if ((double)i / phi[i] < maxx && check(i)) {
maxx = (double)i / phi[i];
ans = i;
}
}
cout << ans << " " << maxx << "\n";
return 0;
}

浙公网安备 33010602011771号