欧拉计划 91~100
91
这个题的扩展满足 \(N \le 2500\),枚举每个点的位置去计算太慢,我们能否通过枚举一条线来计算答案?
考虑枚举一条斜率为 \(\frac{y}{x}\) 的一条直线,这条直线在至少可以在范围内交出 \((x, \, y)\) 这个点,我们以 \((0, \, 0), \, (x, \, y)\) 为一条直角边,显然只有过 \((x, \, y)\) 做这条直线的垂线所过的整数点是合法的,我们只考虑一侧,因为另一侧是等价的。
设 \(d = \gcd(x, \, y)\) 也就是说这样的点应该横坐标每增加 \(\frac{y}{d}\) 纵坐标就减少 \(\frac{x}{d}\),那么合法点的个数为
化简可得
对称相当于乘二,然后加上与 \(xOy\) 坐标轴平行的直角三角形个数 \(3N^2\) 即可。
// cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
int ans = 0, N = 50;
for (int x = 1; x <= N; x ++ ) {
for (int y = 1; y <= N; y ++ ) {
int d = __gcd(x, y);
ans += min((N - x) * d / y, y * d / x);
}
}
cout << ans * 2 + N * N * 3;
return 0;
}
92
我们把相互转换的数链接一条边,利用并查集,我们可以把数分为属于 \(1\) 或属于 \(89\) 的,如此可以计算。
// cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e7 + 5;
int n = 1e7, p[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
for (int i = 1; i <= n; i ++ ) p[i] = i;
for (int i = 1; i <= n; i ++ ) {
int t = 0, x = i;
while (x) t += (x % 10) * (x % 10), x /= 10;
p[find(i)] = find(t);
}
int ans = 0;
for (int i = 1; i <= n; i ++ ) {
if (find(i) == find(89)) ans ++ ;
}
cout << ans;
return 0;
}
93
恶心大模拟,枚举每次选择的数对进行操作即可。
// cpp
#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-6;
set<int> s;
vector<int> ans = {1, 2, 3, 4, 0};
void calc(vector<double> num) {
if (num.size() == 1) {
if (abs(num[0] - round(num[0])) < eps) {
s.insert(round(num[0]));
}
return;
}
for (int i = 0; i < num.size(); i ++ ) {
for (int j = i + 1; j < num.size(); j ++ ) {
vector<double> _num, __num;
for (int k = 0; k < num.size(); k ++ ) {
if (k == i || k == j) continue;
_num.push_back(num[k]);
}
__num = _num;
__num.push_back(num[i] + num[j]), calc(__num), __num.pop_back();
__num.push_back(num[i] - num[j]), calc(__num), __num.pop_back();
__num.push_back(num[j] - num[i]), calc(__num), __num.pop_back();
__num.push_back(num[i] * num[j]), calc(__num), __num.pop_back();
__num.push_back(num[i] / num[j]), calc(__num), __num.pop_back();
__num.push_back(num[j] / num[i]), calc(__num), __num.pop_back();
}
}
}
void generator(int a, int b, int c, int d) {
vector<int> num = {a, b, c, d};
s.clear();
vector<double> _num;
for (auto c : num) _num.push_back((double)c);
calc(_num);
for (int i = 1; ; i ++ ) {
if (!s.count(i)) {
if (i - 1 > ans[4]) {
ans = vector<int>{num[0], num[1], num[2], num[3], i - 1};
}
break;
}
}
}
int main() {
for (int d = 4; d <= 10; d ++ ) {
for (int c = 3; c < d; c ++ ) {
for (int b = 2; b < c; b ++ ) {
for (int a = 1; a < b; a ++ ) {
generator(a, b, c, d);
}
}
}
}
sort(ans.begin(), ans.end());
for (int i = 0; i < 4; i ++ ) cout << ans[i];
cout << "\n" << ans[4];
return 0;
}
94
根据题意,设三边为 \(y, \, y, \, x\),我们容易列出三角形的面积公式:
不难发现,\(x\) 为偶数,且 \(\frac{x}{2}, \, y\) 为一组本原毕达哥拉斯三元组,否则差值至少为 \(2\)。
我们考虑 本原毕达哥拉斯三元组生成树 如下:

通过验证小数据,我们发现以下的组是符合条件的:
- \((3, \, 4, \, 5)\)
- \((15, \, 8, \, 17)\)
- \((33, \, 56, \, 65)\)
- \((209, \, 120, \, 241)\)
- \(\cdots\)
不难发现在树上路径为 \(1 \to 3 \to 1 \to \cdots\),我们记转移矩阵
轮换交替转移即可。
对于符合条件的答案,我们将最短边与最长边之和乘二即可,因为这在三角形内恰好占据一半,如此可以解决。
# python
from numpy import *
A = matrix([[1, -2, 2],
[2, -1, 2],
[2, -2, 3]])
C = matrix([[-1, 2, 2],
[-2, 1, 2],
[-2, 2, 3]])
pythagorean = matrix([[3], [4], [5]])
limit = 1e9
switch = 0
ans = 0
while sum(pythagorean) <= limit:
ans += max(pythagorean) * 2 + min(pythagorean) * 2
if switch:
pythagorean = A * pythagorean
switch = 0
else:
pythagorean = C * pythagorean
switch = 1
print(ans)
95
筛完因数和之后判环的一个过程,这里采用了 tarjan 算法来缩点判环,因为图中不含任何复合环。
// cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int primes[N], p[N], d[N], st[N], cnt;
int dfn[N], low[N], timestamp;
int stk[N], in_stk[N], id[N], sz[N], top, scc_cnt;
int n = 1e6;
void tarjan(int u) {
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u, in_stk[u] = 1;
if (d[u] <= n && !dfn[d[u]]) {
tarjan(d[u]);
low[u] = min(low[u], low[d[u]]);
} else if (d[u] <= n && in_stk[d[u]]) {
low[u] = min(low[u], dfn[d[u]]);
}
if (dfn[u] == low[u]) {
++ scc_cnt;
int y;
do {
y = stk[top -- ];
in_stk[y] = 0;
id[y] = scc_cnt;
sz[scc_cnt] ++ ;
} while (y != u);
}
}
int main() {
for (int i = 2; i < N; i ++ ) {
if (!st[i]) primes[cnt ++ ] = i, p[i] = d[i] = i + 1;
for (int j = 0; i * primes[j] < N; j ++ ) {
st[i * primes[j]] = 1;
if (i % primes[j] == 0) {
d[i * primes[j]] = d[i] / p[i] * (p[i] * primes[j] + 1);
p[i * primes[j]] = p[i] * primes[j] + 1;
break;
}
d[i * primes[j]] = d[i] * (primes[j] + 1);
p[i * primes[j]] = primes[j] + 1;
}
}
for (int i = 2; i < N; i ++ ) d[i] -= i;
for (int i = 1; i <= n; i ++ )
if (!dfn[i])
tarjan(i);
int len = 6, ans = 1;
for (int i = 1; i <= n; i ++ ) {
if (sz[id[i]] > len) {
len = sz[id[i]];
ans = i;
}
}
cout << len << " " << ans << "\n";
return 0;
}
96
道理我都懂,为什么这个数独跑这么快啊。
// cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 10;
int row[N], col[N], block[N];
int a[N * N];
int ans = 0;
int calc(int x, int y) {
return 9 * x + y;
}
int to_block(int x, int y) {
return x / 3 * 3 + y / 3;
}
void dfs(int pos) {
if (pos == 81) return ans += a[0] * 100 + a[1] * 10 + a[2], void();
if (a[pos]) return dfs(pos + 1), void();
int x = pos / 9, y = pos % 9;
for (int i = 0; i < 9; i ++ ) {
if (row[x] >> i & 1) continue;
if (col[y] >> i & 1) continue;
if (block[to_block(x, y)] >> i & 1) continue;
a[pos] = i + 1;
row[x] |= 1 << i;
col[y] |= 1 << i;
block[to_block(x, y)] |= 1 << i;
dfs(pos + 1);
a[pos] = 0;
row[x] ^= 1 << i;
col[y] ^= 1 << i;
block[to_block(x, y)] ^= 1 << i;
}
}
int main() {
string s;
int n = 50;
while (n -- ) {
cin >> s >> s;
memset(row, 0, sizeof row);
memset(col, 0, sizeof col);
memset(block, 0, sizeof block);
memset(a, 0, sizeof a);
for (int i = 0; i < 9; i ++ ) {
cin >> s;
for (int j = 0; j < 9; j ++ ) {
a[calc(i, j)] = s[j] - '0';
if (!a[calc(i, j)]) continue;
row[i] |= 1 << a[calc(i, j)] - 1;
col[j] |= 1 << a[calc(i, j)] - 1;
block[to_block(i, j)] |= 1 << a[calc(i, j)] - 1;
}
}
dfs(0);
}
cout << ans << "\n";
return 0;
}
97
人生苦短,我用 python
# python
print(28433 * pow(2, 7830457, int(1e10)) % int(1e10) + 1)
98
疑似是有点司马了的大模拟。
把字母相同的串处理出来,枚举每个相通字符串的对,再枚举合法的平方数看看能不能置换产生令一个合法的平方数。
// cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
unordered_map<ll, ll> value;
vector<string> sqr[15];
int main() {
for (ll i = 1; i <= 1e6; i ++ ) {
ll x = i * i;
string p = "", q = "";
while (x) {
p += x % 10 + '0';
x /= 10;
}
reverse(p.begin(), p.end());
sqr[p.size()].push_back(p);
value[i * i] = 1;
}
vector<string> ary = {/* file.txt */};
vector<string> ary_copy = ary;
for (auto &items : ary) sort(items.begin(), items.end());
sort(ary.begin(), ary.end());
ll ans = 0;
vector<string> useful;
for (int k = 0; k + 1 < ary.size(); k ++ ) {
if (ary[k] == ary[k + 1]) {
while (k + 1 < ary.size() && ary[k] == ary[k + 1]) k ++ ;
for (int i = 0; i < ary_copy.size(); i ++ ) {
string it = ary_copy[i];
sort(it.begin(), it.end());
if (it == ary[k]) useful.push_back(ary_copy[i]);
}
for (int i = 0; i < useful.size(); i ++ ) {
for (int j = i + 1; j < useful.size(); j ++ ) {
for (auto items : sqr[useful[i].length()]) {
unordered_map<char, ll> rep;
unordered_map<ll, char> r_rep;
int pd = 1;
for (int l = 0; l < items.length(); l ++ ) {
ll x = items[l] - '0';
if (rep.count(useful[i][l]) && rep[useful[i][l]] != x) {
pd = 0;
break;
}
if (r_rep.count(x) && rep[x] != useful[i][l]) {
pd = 0;
break;
}
rep[useful[i][l]] = x;
r_rep[x] = rep[useful[i][l]];
}
if (!pd) continue;
if (rep[useful[j][0]] == 0) continue;
ll x = 0;
for (int l = 0; l < useful[j].length(); l ++ ) {
x = x * 10 + rep[useful[j][l]];
}
if (value.count(x)) {
// cout << useful[i] << " " << useful[j] << " " << x << "\n";
ans = max(ans, x);
}
}
}
}
useful.clear();
}
}
cout << ans;
return 0;
}
数据自查
BOARD BROAD 18769
CARE RACE 9216
CARE RACE 1296
DEAL LEAD 4761
DEAL LEAD 1764
HATE HEAT 1936
MALE MEAL 1936
MEAN NAME 1024
MEAN NAME 4096
RATE TEAR 2401
RATE TEAR 9604
EAST SEAT 1296
EAT TEA 625
EAT TEA 196
DOG GOD 961
DOG GOD 169
FILE LIFE 9216
FILE LIFE 1296
NOTE TONE 9216
NOTE TONE 1296
HOW WHO 625
HOW WHO 196
SHUT THUS 4761
SHUT THUS 1764
ITS SIT 625
ITS SIT 196
NOW OWN 961
NOW OWN 256
POST SPOT 1296
POST STOP 2401
POST STOP 9604
18769
99
map 好用,键值自动排序,取对数后放进去就行了。
// cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<double, int> mp;
int main() {
int n = 1000;
for (int i = 0; i < n; i ++) {
ll a, b;
scanf("%lld,%lld", &a, &b);
mp[b * log2(a)] = i + 1;
}
for (auto items : mp) {
cout << items.first << " " << items.second << "\n";
}
return 0;
}
100
方程
的正整数解使我们的答案,展开不难得到
我们把这个式子凑成佩尔方程的情形
容易看出,这个方程是 第二类佩尔方程 。
记 \(x_0 = 2a_0 - 1, \, y_0 = 2b_0 - 1\),对于经典佩尔方程而言,可以通过 \((x_0 - dy_0)^n\) 给出 \(n\) 阶通解形式。
因为如果 \(\alpha_1^2 - d\beta_1^2 = T, \, \alpha_2^2 - d\beta_2^2 = T\) 为两组整数解,那么 \((\alpha_1^2 - d\beta_1^2)(\alpha_2^2 - d\beta_2^2) = T^2\) 显然也是该方程一组解。
展开可以得到
配方可得
取 \(T = -1\) 时,若 \(x_0, \, y_0\) 为方程的基础解,设 \(K = (x_0^2 + dy_0^2)^2 = 1\),则 \(K = K_x^2 - dK_y^2 = (x_0^2 + dy_0^2)^2 - d(2x_0y_0)^2 = 1\)
则由解 \((x_0, \, y_0)\) 可递推后继解,考虑转移矩阵 \(X\) 满足
则
而满足 \(x^2 - 2y^2 = -1\) 的最小解 \((x_0, \, y_0) = (1, \, 1)\),我们可以求出所有的 \(x, \, y\),并带入 \(a = \frac{x + 1}{2}, \, b = \frac{y + 1}{2}\) 即可验证。
关于解的有限性和唯一性可以参见 这篇文章,这里不再给出证明。
# python
from sympy import *
B = Matrix([1, 1]) # column vector
K = Matrix([3, 2])
X = Matrix([[3, 4], [2, 3]])
while (B[0] + 1) // 2 < 1e12:
B = X * B
print((B[0] + 1) // 2, (B[1] + 1) // 2)

浙公网安备 33010602011771号