A 空間
主要考察計算機的基礎知識計算機內部存儲的換算如下
於是有
所以可裝入256*1024*1024*8 / 32 = 67108864
個
答案:
67108864
B 卡片
簡單模擬
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 20;
int a[N];
int main()
{
for (int i = 0; i < 10; i ++) a[i] = 2021;
int flag = 0;
for (int i = 1; ; i ++) {
int k = i;
while (k) {
int x = k % 10;
if (!a[x]) flag = 1;
a[x] --;
k /= 10;
}
if (flag) { cout << i - 1; break; }
}
return 0;
}
答案:
3181
C 直線
問題描述
暴力低精度解法
考慮double
的精度,本題最小的斜率為\(\frac{1} {20} = 0.05\)顯然對於double
來說精度足夠。
根據直線方程\(y=kx+b\)可以通過兩個點來求出對應的\(k\)和\(b\),只要加入到set
去重即可。特別要注意的是,斜率和截距應該使用double
本題數據量較小,所以不用考慮精度問題可能過
#include<bits/stdc++.h>
#define x first
#define y second
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<double, double> PII;
set<PII> se;
int main ()
{
int n, m;
double t, c;
n = 20, m = 21;
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j ++) {
PII a = {i, j};
for (int k = 0; k < n; k ++)
for (int l = 0; l < m; l ++) {
PII b = {k, l};
if (a.x == b.x && a.y == b.y) continue;
if (!(b.x-a.x)) { t = INF; c = a.x; se.insert({t, c}); }
else {
t = (b.y-a.y)*1.0 / (b.x-a.x)*1.0;
c = (b.x*a.y - a.x*b.y)*1.0 / (b.x-a.x)*1.0;
se.insert({t, c});
}
}
}
}
cout << se.size() << endl;
return 0;
}
答案:
40257
D貨物擺放
問題描述
解答
先分析一下\(n\)含有多少個因子
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<LL> vec;
int main()
{
LL n = 2021041820210418;
for (LL i = 1; i * i <= n; i ++) {
if (n % i == 0) {
vec.push_back(i);
LL k = n / i;
if (k != i) vec.push_back(k);
}
}
cout << vec.size();
return 0;
}
可以得出\(n\)僅有128
個因子,所以直接枚舉即可
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<LL> vec;
int main()
{
LL n = 2021041820210418;
for (LL i = 1; i * i <= n; i ++) {
if (n % i == 0) {
vec.push_back(i);
LL k = n / i;
if (k != i) vec.push_back(k);
}
}
LL ans = 0;
for (auto a : vec)
for (auto b : vec)
for (auto c : vec)
if (a * b * c == n) ans ++;
cout << ans;
return 0;
}
答案:
2430
E 路徑
問題描述
題解
歐幾里得算法
解決這道題會用到最小公約數所以先來復習一下求最大公約數的歐幾里德算法,歐幾里得算法的核心其實是\(gcd(a, b) = gcd(b, a\ mod\ b)\)下面進行證明
-
對\(a\ mod \ b\)進行變換
\[\begin{align*} a\ mod\ b &= a - \left \lfloor \frac{a}{b} \right \rfloor \times b\\ &=a - c\times b \end{align*} \] -
證明對於\(a\)和\(b\)的任意公約數\(k\),都是\(b\)和\(a\ mod\ b\)的公約數
是\(b\)的公約數,同時也是\(a-c\times b\) 的公約數 -
證明對於\(b\)和\(a\ mod\ b\)的任意公約數\(m\),都是\(a\)和\(b\)的公約數
即證明\(m\)是\(a\)的公約數,\(m\)可以整除\(a\ mod\ b\),則\(m\)可以整除\(a-c\times b\)所以\(m\)可以整除\(a\)
綜上所述,集合\(cd(a, b)\)等於集合\(cd(b, a\ mod\ b)\),則\(gcd(a, b) = gcd(b, a\ mod\ b)\),該過程的實現如下
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
根據題目描述用\(dijkstra\)算法求最短路即可。由於題中邊數\(21\times 2021 = 42441\)點數為\(2021\)所以用朴素版的\(dijkstra\)算法即可時間復雜度為\(O(n^2)\)
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 3000;
int g[N][N], d[N];
bool st[N];
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int dijkstra(int n) {
memset(d, INF, sizeof d);
d[1] = 0;
for (int i = 0; i < n; i ++) {
int k = INF, x;
for (int y = 1; y <= n; y ++) if (!st[y] && d[y] < k) k = d[x = y];
st[x] = true;
for (int y = 1; y <= n; y ++) d[y] = min(d[y], d[x] + g[x][y]);
}
if (d[n] == INF) return -1;
return d[n];
}
int main()
{
memset(g, INF, sizeof g);
for (int i = 1; i <= 2021; i ++)
for (int j = i + 1; j <= i + 21 && j <= 2021; j ++) {
g[i][j] = g[j][i] = i*j / gcd(i, j);
}
cout << dijkstra(2021);
return 0;
}
答案:
10266837
楊輝三角
暴力解法
由於題目中\(N\)的最大值為\(10^9\)暴力解法的時間復雜度為\(O(n^2)\)所以必然超時,結果只能過\(40\%\)
#include<bits/stdc++.h>
using namespace std;
const int N = 65000000;
int c[N];
int main ()
{
int ans = 0, t;
int flag = 0;
cin >> t;
for (int m = 1; m < N; m ++) {
c[0] = 1;
ans ++;
if (c[0] == t) break;
for (int i = 1; i <= m; i ++) {
ans ++;
c[i] = c[i-1] * (m-i+1) / i;
if (c[i] == t) { ans ++; flag = 1; break; }
}
if (flag) break;
}
cout << ans << endl;
return 0;
}
AC解法
組合數求法
LL C(int a, int b) {
int res = 1;
for (int i = a, j = 1; j <= b; j ++, i--) {
res = res * i / j;
if (res > n) return res; // 防止數值過大
}
return res;
}
分析
以中間為分割線,由於是從上至下從左至右進行計數,又由於楊輝三角是左右對稱的所以第一次出現\(N\)只有可能是在左側所以我們只考慮左側的數。進一步通過觀察發現,楊輝三角的每一行是遞增的如果斜着觀察如下圖所示。
結果是遞增的。右下角的數值應該是最大值,所以我們從內向外斜着枚舉。觀察到\(C^{17}_{34} > 10^9\)而\(C^{16}_{32}<10^9\)所以只需要從第16層斜着枚舉即可,最后時間復雜度為\(16\times O(logN)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
LL C(int a, int b) {
LL res = 1;
for (int i = a, j = 1; j <= b; j ++, i --) {
res = res * i / j;
if (res > n) return res;
}
return res;
}
int main()
{
cin >> n;
for (int k = 16; ; k --) {
LL l = 2 * k, r = n;
while (l < r) {
int mid = l + r >> 1;
if (C(mid, k) >= n) r = mid;
else l = mid + 1;
}
if (C(l, k) == n) { cout << (1 + l) * l / 2 + k + 1 << endl; break; }
}
return 0;
}
砝碼稱重
暴力解法
直接爆搜枚舉出所有的可能,當然只能過掉\(40\%\)的數據。
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int w[N], n, ans;
set<int> st;
void dfs(int u, int weight) {
if (u == n) {
st.insert(weight);
return;
}
dfs(u + 1, abs(weight - w[u])); // 右邊放w[u]的砝碼
dfs(u + 1, weight); // 不放左右兩邊都不放
dfs(u + 1, abs(weight + w[u])); // 左邊放w[u]的砝碼
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++) scanf("%d", &w[i]);
dfs(0, 0);
cout << st.size() - 1 << endl;
return 0;
}