2024天梯選拔賽(一)
A 私人笑聲
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string str;
getline(cin, str);
for (int i = 0; i < str.size(); i ++) {
cout << str[i] ;
if (str[i] == '.')
cout << "xixixixi.";
}
return 0;
}
B 孵化小雞
資料小,dfs
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, M;
cin >> n >> M;
vector<int> a(n), b(n), m(n), l(m), r(m), k(m), p(m);
for (int i = 0; i < n; i ++)
cin >> a[i] >> b[i] >> m[i];
for (int i = 0; i < M; i ++)
cin >> l[i] >> r[i] >> k[i] >> p[i];
vector<bool> vis(M);
vector<int> lr(200);
i64 ans = INT_MAX;
auto dfs = [&](auto self, int num, i64 res) {
if (num == M) {
bool ok = true;
for (int i = 0; i < n; i ++) {
if (!ok) break;
for (int j = a[i]; j <= b[i]; j ++) {
if (lr[j] < m[i]) {
ok = false;
break;
}
}
}
if (ok) ans = min(ans, res);
return ;
}
self(self, num + 1, res);
for (int i = l[num]; i <= r[num]; i ++)
lr[i] += k[num];
self(self, num + 1, res + p[num]);
for (int i = l[num]; i <= r[num]; i ++)
lr[i] -= k[num];
};
dfs(dfs, 0, 0);
cout << ans << '\n';
return 0;
}
C 可怕的凍雨
考慮離線;
將落腳點按光滑程度排序,以及雪地靴按防滑程度排序;
預處理出能夠直接到達的落腳點記錄並儲存兩兩點之間的距離;
遍歷雪地靴,判斷雪地靴能否在原有的落腳點上新增落腳點,能夠新增則找到新增點的鄰近點,更新原有的距離,最後判斷雪地靴的行走距離能否跨過最遠的冰層;
防滑程度小的雪地靴能到達的落腳點,防滑度大的可延續其之前的路線;
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<PII> A;
for (int i = 1, x; i <= n; i ++) {
cin >> x;
A.emplace_back(x, i);
}
vector<array<int, 3>> B;
for (int i = 0, k, s; i < m; i ++) {
cin >> k >> s;
B.push_back({k, s, i});
}
sort(A.begin(), A.end());
sort(B.begin(), B.end());
set<int> loc;
multiset<int> dis;
int index = 0;
while (index < A.size() && !A[index].first)
loc.insert(A[index].second), index ++;
for (auto it = loc.begin(); next(it) != loc.end(); it = next(it))
dis.insert(*next(it) - *it);
vector<int> ans(m);
for (auto [k, s, id] : B) {
while (index < A.size() && A[index].first <= k) {
int i = A[index].second;
index ++;
auto t = loc.upper_bound(i);
int r = *t, l = *(prev(t));
loc.insert(i);
dis.erase(dis.find(r - l));
dis.insert(i - l);
dis.insert(r - i);
}
ans[id] = (s >= *dis.rbegin());
}
for (auto i : ans)
cout << i << '\n';
return 0;
}
D 劃分田地(easy)
列舉矩形,將在矩形內的點加入進去;
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<PII> tu(n);
for (auto &[x, y] : tu)
cin >> x >> y;
set<vector<int>> ans;
for (int i = 0; i <= 50; i ++) {
for (int j = 0; j <= 50; j ++) {
for (int k = i; k <= 50; k ++) {
for (int p = j; p <= 50; p ++) {
vector<int> ve;
int num = 0;
for (auto [x, y] : tu) {
num ++;
if (x >= i && x <= k && y >= j && y <= p)
ve.push_back(num);
}
ans.insert(ve);
}
}
}
}
cout << ans.size() << '\n';
return 0;
}
E 劃分田地(hard)
對於一個矩形,如果它的上邊掃描到上邊界這個矩形中有\(x\)個點,下邊掃描到下邊界的這個矩形中有\(y\)個點,那麼這個矩形可以向上向下擴充邊界從而可以得到\((x + 1) \times (y + 1)\)個包含不同點的矩形(向左向右也是一樣);
本題中,我採用的是上下擴充;
將座標二維離散後,運用二維字首和計算上矩形和下矩形中分別有多少個點,從而累加計算結果, 最後加上\(n+1\)是\(n\)個點和一個空集;
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
struct Two_D_Discrete {
int n, tot1 = 1, tot2 = 1;
vector<vector<int>> mp;
vector<int> x, y, nx, ny;
vector<pair<i64, i64>> a;
vector<PII> New;
Two_D_Discrete (int _n, vector<pair<i64, i64>>& _a): n(_n), a(_a) {
x.resize(n), y.resize(n);
nx.resize(n * 2 + 5), ny.resize(n * 2 + 5);
vector<vector<int>>(n * 2 + 5, vector<int>(n * 2 + 5)).swap(mp);
for (int i = 0; i < n; i ++) {
x[i] = a[i].first;
y[i] = a[i].second;
}
}
void work() {
//排序
sort(x.begin(), x.end());
sort(y.begin(), y.end());
// 去重 並得到有多少個點
int len1 = unique(x.begin(), x.end()) - x.begin();
int len2 = unique(y.begin(), y.end()) - y.begin();
// 離散化 x 軸
for (int i = 0; i < len1; i++) {
if (i && x[i] != x[i - 1] + 1)
nx[tot1++] = x[i] - 1, nx[tot1++] = x[i];
else
nx[tot1++] = x[i];
}
// 離散化 y 軸
for (int i = 0; i < len2; i++) {
if (i && y[i] != y[i - 1] + 1)
ny[tot2++] = y[i] - 1, ny[tot2++] = y[i];
else
ny[tot2++] = y[i];
}
//對映關係將需離散的點放入離散圖中
for (int i = 0; i < n; i++) {
int newx = lower_bound(nx.begin(), nx.begin() + tot1, a[i].first) - nx.begin();
int newy = lower_bound(ny.begin(), ny.begin() + tot2, a[i].second) - ny.begin();
mp[newx][newy] = 1;
// cout << "(" << newx << ',' << newy << ")\n";
New.emplace_back(newx, newy);
}
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<PII> a(n);
for (auto &[x, y] : a) {
cin >> x >> y;
x ++, y ++;
}
Two_D_Discrete _2D(n, a);
_2D.work();
int Ke = n * 2 + 5;
vector sum(Ke, vector<int>(Ke));
for (int i = 1; i < Ke; i ++)
for (int j = 1; j < Ke; j ++)
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + _2D.mp[i][j];
auto calc = [&](int a, int b, int c, int d) {
return sum[c][d] - sum[a - 1][d] - sum[c][b - 1] + sum[a - 1][b - 1];
};
i64 ans = 0;
for (int i = 0; i < n; i ++) {
for (int j = i + 1; j < n; j ++) {
auto [x1, y1] = _2D.New[i];
auto [x2, y2] = _2D.New[j];
if (x1 > x2) swap(x1, x2);
if (y1 > y2) swap(y1, y2);
int num1 = calc(1, y1, x1 - 1, y2);
int num2 = calc(x2 + 1, y1, Ke - 1, y2);
ans += (num1 + 1) * (num2 + 1);
}
}
cout << ans + n + 1 << '\n';
return 0;
}
F 加一餘二
用set儲存連續相同子串區間,multiset儲存其區間長度;
對於操作的下標\(x\),要去判斷子串區間中所在的位置,進行分裂與合併;
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
int m;
cin >> s >> m;
multiset<int> ans;
set<PII> res;
for (int i = 1, j; i <= s.size(); i = j + 1) {
j = i;
while (j < s.size() && s[j] == s[i - 1]) j ++;
res.insert(PII(i, j));
ans.insert(j - i + 1);
}
auto len = [](PII x) {
return x.second - x.first + 1;
};
auto Find = [&](int x) {
auto t = res.lower_bound(PII(x, -1));
if (t == res.end() || (*t).first > x) t = prev(t);
return *t;
};
while (m --) {
int x;
cin >> x;
PII t = Find(x);
res.erase(res.find(t));
ans.erase(ans.find(len(t)));
if (x != 1) {
if (t.first == x) {
PII p = Find(x - 1);
res.erase(res.find(p));
ans.erase(ans.find(len(p)));
t.first = p.first;
} else {
PII p = {t.first, x - 1};
t.first = x;
res.insert(p);
ans.insert(len(p));
}
}
if (x != s.size()) {
if (t.second == x) {
PII p = Find(x + 1);
res.erase(res.find(p));
ans.erase(ans.find(len(p)));
t.second = p.second;
} else {
PII p = {x + 1, t.second};
t.second = x;
res.insert(p);
ans.insert(len(p));
}
}
res.insert(t);
ans.insert(len(t));
cout << *ans.rbegin() << ' ';
}
return 0;
}
G 相加餘三(easy)
本題分別模擬三種情況然後取最大值即可。
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
for (auto &i : a) cin >> i;
i64 ans1 = 0 , ans2 = 0 , ans3 = 0 ;
for (int i = 0; i < n; i += 2) {
ans1 += ((a[i] + a[i + 1]) % 3);
}
for (int i = n; i >= 0; i -= 2) {
ans2 += ((a[i] + a[i - 1]) % 3);
}
for (int i = 0, j = n - 1; i < j; i ++, j --) {
ans3 += ((a[i] + a[j]) % 3);
}
cout << max({ans1, ans2, ans3}) << '\n';
return 0;
}
H 相加餘三(hard)
考慮區間\(dp\);
從大區間往小區間轉移,分奇偶討論一下是因為每次去掉偶數個數,所以對於一個區間,到左右邊界的值為奇數時是不合法的;
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++)
cin >> a[i];
vector dp(n + 1, vector<int>(n + 1));
int ans = 0;
for (int i = 1; i <= n; i ++) {
for (int j = n; j > i; j --) {
if (!((j - i) & 1) && n % 2 == 0) continue;
if (((j - i) & 1) && (n & 1)) continue;
if (i + 1 <= n) ans = max(ans, dp[i][i + 1] + (a[i] + a[i + 1]) % 3);
if (j >= i + 2) {
dp[i + 2][j] = max(dp[i + 2][j], dp[i][j] + (a[i] + a[i + 1]) % 3);
dp[i][j - 2] = max(dp[i][j - 2], dp[i][j] + (a[j - 1] + a[j]) % 3);
dp[i + 1][j - 1] = max(dp[i + 1][j - 1], dp[i][j] + (a[i] + a[j]) % 3);
}
}
}
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++)
ans = max(ans, dp[i][j]);
cout << ans << '\n';
return 0;
}
從小區間往大區間轉移,預處理小區間的值
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++)
cin >> a[i];
vector dp(n + 1, vector<int>(n + 1));
int ans = 0;
for (int len = 2; len <= n; len ++) {
for (int i = 1, j = len; j <= n; j ++, i ++) {
if (len == 2)
dp[i][j] = (a[i] + a[j]) % 3;
else {
dp[i][j] = max(dp[i][j], dp[i + 2][j] + (a[i] + a[i + 1]) % 3);
dp[i][j] = max(dp[i][j], dp[i][j - 2] + (a[j] + a[j - 1]) % 3);
dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + (a[i] + a[j]) % 3);
}
}
}
cout << dp[1][n] << '\n';
return 0;
}
I 找除數
一個正整數\(n\)可以表示為\(n=p_1^{x_1} \times p_2^{x_2} \times p_3^{x_3} \times p_4^{x_4} \dots\)(其中\(p_i\)為質數)
則\(n\)的除數的數量$=(x_1 + 1) \times (x_2 + 1) \times (x_3 + 1) \times (x_4 + 1) \times \dots $
預處理出10000以內的質數;
然後將\(n\)按質數分解,按照上述公式計算即可;
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
vector<int> prime, isnp(10005);
void solve() {
int n;
cin >> n;
int ans = 1;
for (auto k : prime) {
if (n % k == 0) {
int cnt = 1;
while (n % k == 0) n /= k, cnt ++;
ans *= cnt;
}
}
if (n != 1) ans *= 2;
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
for (int i = 2; i <= 10000; i ++) {
if (!isnp[i]) prime.push_back(i);
for (auto k : prime) {
if (k * i > 10000) break;
isnp[i * k] = 1;
if (i % k == 0) break;
}
}
int T;
cin >> T;
while (T --)
solve();
return 0;
}
J 最後都是0
\(j\)為整數\(n\)上的某一位數字.
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 1; i <= n; i ++) {
string s = to_string(i);
for (auto j : s) {
dp[i] = min(dp[i], dp[i - (j - '0')] + 1);
}
}
cout << dp[n] << '\n';
return 0;
}
K 第五人格,啟動!
包括起點和終點一共五個點,計算出兩兩點之間的距離,用全排列表示出所有路線,然後取最短時間
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
int u[] = {1, -1, 0, 0}, v[] = {0, 0, 1, -1};
vector<string> mp(n);
for (auto &i : mp) cin >> i;
int sx, sy, ex, ey;
vector<int> t(3);
vector<PII> xy(3);
cin >> sx >> sy ;
sx --, sy --;
for (auto &[x, y] : xy) {
cin >> x >> y;
x--, y--;
}
cin >> ex >> ey;
ex--, ey--;
for (auto &i : t) cin >> i;
vector dis(5, vector<i64>(5, 0));
for (int i = 0; i < 3; i ++) {
auto [x, y] = xy[i];
dis[0][i + 1] = abs(x - sx) + abs(y - sy);
}
for (int i = 0; i < 3; i ++)
for (int j = 0; j < 3; j ++)
dis[i + 1][j + 1] = (abs(xy[i].first - xy[j].first) + abs(xy[i].second - xy[j].second));
auto bfs = [&](int x, int y, int edx, int edy) -> i64{
vector<bitset<110>> vis(m);
queue<pair<PII, i64>> Q;
Q.push({{x, y}, 0});
vis[x][y] = 1;
int res = 0;
while (Q.size()) {
auto [axy, w] = Q.front();
auto [ax, ay] = axy;
Q.pop();
if (ax == edx && ay == edy) {
res = w;
break;
}
for (int i = 0; i < 4; i ++) {
int dx = u[i] + ax;
int dy = v[i] + ay;
if (dx >= 0 && dx < n && dy >= 0 && dy < m && !vis[dx][dy] && mp[dx][dy] != '#') {
Q.push({{dx, dy}, w + 1});
vis[dx][dy] = 1;
}
}
}
return res;
};
for (int i = 0; i < 3; i ++) {
auto [x, y] = xy[i];
dis[i + 1][4] = bfs(x, y, ex, ey);
}
i64 ans = LLONG_MAX;
vector<vector<int>> st;
vector<int> sh{1, 2, 3};
do {
vector<int> jk{0};
for (int i = 0; i < 3; i ++) {
jk.push_back(sh[i]);
}
jk.push_back(4);
st.push_back(jk);
} while (next_permutation(sh.begin(), sh.end()));
for (auto v : st) {
i64 res = 0, p = 1;
for (int i = 1; i < 5; i ++) {
res += dis[v[i - 1]][v[i]] * p;
if (p != 4) p += t[v[i] - 1];
}
ans = min(ans, res);
}
cout << ans << '\n';
return 0;
}
L 迦納~
已知影片一共完整播放了 k / n 遍,然後我們需要判斷一下剩下的時間是否大於 m 秒即可。
#include <bits/stdc++.h>
#define debug(a) cout<<#a<<"="<<a<<'\n';
using namespace std;
using i64 = long long;
typedef pair<i64, i64> PII;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
i64 n, m, k;
cin >> n >> m >> k;
cout << k / n + (k % n >= m) << '\n';
return 0;
}