2024牛客寒假算法基础集训营2

1|0A-Tokitsukaze and Bracelet


分类讨论

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using i128 = __int128; using ldb = long double; #define int i64 using vi = vector<int>; using pii = pair<int, int>; using vii = vector<pii>; const int inf = INT_MAX, INF = LLONG_MAX; const int mod = 998244353; const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0}; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n; cin >> n; for (int a, b, c, res; n; n--) { cin >> a >> b >> c, res = 0; res += (a - 100) / 50; if (34 <= b and b <= 40) res += 1; else if (b == 45) res += 2; if (34 <= c and c <= 40) res += 1; else if (c == 45) res += 2; cout << res << "\n"; } return 0; }

2|0B-Tokitsukaze and Cats


其实就是特判每只周围有没有猫即可

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using i128 = __int128; using ldb = long double; #define int i64 using vi = vector<int>; using pii = pair<int, int>; using vii = vector<pii>; const int inf = INT_MAX, INF = LLONG_MAX; const int mod = 998244353; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n, m, k; cin >> n >> m >> k; vector e(n + 1, vi(m + 1)); for (int i = 1, x, y; i <= k; i++) cin >> x >> y, e[x][y] = 1; int res = k * 4; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { if (e[i][j] != 1) continue; if (i + 1 <= n and e[i + 1][j]) res--; if (j + 1 <= m and e[i][j + 1]) res--; } cout << res << "\n"; return 0; }

3|0C-Tokitsukaze and Min-Max XOR


因为是求子序列,所以可以对a进行排序。

排序后如果选择了[l,r],则min=al,max=ar,而中间的数可以随便选,所以贡献就是2rl1

这样枚举端点的话复杂度是O(n2),我们先考虑2rl1=2r12l,因此对于一个复合条件的r所有合法的l贡献和为2r112l,所以我们可以枚举r然后快速的求和

我们可以考虑用 01Tire来维护这个信息,首先就是把所有的二进制串对其,然后后对于每插入一个串,其在路径上的贡献就是12i

首先xyk,要满足这样的条件xy一定存在一个前缀等于k,然后下一位k1xy0,再之后的位就可以随便取了。

因此我们从根开始查找,如果这一位k1,那么当前位可以异或和0则当前节点子树中的所有串均符合条件,然后向着让当前位异或后和k相同的方向走即可。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int i64 using vi = vector<int>; const int mod = 1e9 + 7; struct Node { int sum = 0; array<Node *, 2> ch; Node() { ch.fill(nullptr); } }; const int N = 2e5; vi pw(N), inv(N); void solve() { int n, k; cin >> n >> k; vector<int> a(n); for (auto &i: a) cin >> i; sort(a.begin(), a.end()); Node *root = new Node(); int res = 0; for (int i = 0; i < n; i++) { auto p = root; int ans = 0; for (int j = 29, w, v; j >= 0 and p != nullptr; j--) { w = (a[i] >> j) & 1, v = (k >> j) & 1; if (v == 1) { if (p->ch[w] != nullptr) ans += p->ch[w]->sum; p = p->ch[w ^ 1]; } else { p = p->ch[w]; } } if (p != nullptr) ans += p->sum; res = (res + 1 + ans % mod * pw[i - 1] % mod) % mod; p = root; for (int j = 29, w; j >= 0; j--) { w = (a[i] >> j) & 1; if (p->ch[w] == nullptr) p->ch[w] = new Node(); p = p->ch[w]; p->sum = (p->sum + inv[i]) % mod; } } cout << res % mod << "\n"; return; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); pw[0] = inv[0] = 1; for (int i = 1; i < N; i++) pw[i] = pw[i - 1] * 2 % mod, inv[i] = inv[i - 1] * (mod + 1) / 2 % mod; int TC; for (cin >> TC; TC; TC--) solve(); return 0; }

4|0D-Tokitsukaze and Slash Draw


每一个操作可以无限次的使用,这样的话就可以当成一个完全背包来做。f[i]表示把一击必杀移动i的最小花费

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using i128 = __int128; using ldb = long double; #define int i64 using vi = vector<int>; using pii = pair<int, int>; using vii = vector<pii>; const int inf = INT_MAX, INF = 1e18; const int mod = 998244353; void solve() { int n, m, k; cin >> n >> m >> k; vi f(n, INF); f[0] = 0; for (int i = 0, a, b, flag; i < m; i++) { cin >> a >> b; do { flag = 0; for (int j = 0, t; j < n; j++) { if (f[j] == INF) continue; t = (j + a) % n; if (f[j] + b < f[t]) f[t] = f[j] + b, flag = 1; } } while (flag); } int res = f[n-k]; if( res == INF ) res = -1; cout << res << "\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int TC; for (cin >> TC; TC; TC--) solve(); return 0; }

5|0E-Tokitsukaze and Eliminate (easy)


其实只要动态维护每个颜色最靠右的位置,以及当前所有最靠右的最小值,然后贪心即可

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using i128 = __int128; using ldb = long double; #define int i64 using vi = vector<int>; using pii = pair<int, int>; using vii = vector<pii>; const int inf = INT_MAX, INF = LLONG_MAX; const int mod = 998244353; const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0}; void solve() { int n; cin >> n; vector<vi> cnt(2); for (int i = 1, x; i <= n; i++) cin >> x, x--, cnt[x].push_back(i); int res = 0; while (not cnt[0].empty() and not cnt[1].empty()) { res++; int i = 0; if (cnt[1].back() < cnt[0].back()) i = 1; while (not cnt[i ^ 1].empty() and cnt[i ^ 1].back() > cnt[i].back()) cnt[i ^ 1].pop_back(); cnt[i].pop_back(); } res += cnt[0].size() + cnt[1].size(); cout << res << "\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int TC; for (cin >> TC; TC; TC--) solve(); return 0; }

6|0F-Tokitsukaze and Eliminate (hard)


这里颜色很多。我们看删除什么样的后缀最优呢?就是恰好包含所有种类的颜色是最优的,因为在往前走一定是重复的,因此必定无法通过一次操作删除。知道这个结论后只要倒序遍历一遍即可。

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using i128 = __int128; using ldb = long double; #define int i64 using vi = vector<int>; using pii = pair<int, int>; using vii = vector<pii>; const int inf = INT_MAX, INF = LLONG_MAX; const int mod = 998244353; const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0}; using edge = array<int, 3>; void solve() { int n; cin >> n; vi a(n), b(n); int cnt = 0, t = 0; for (auto &i: a) cin >> i, i--, b[i]++, cnt += b[i] == 1; reverse(a.begin(), a.end()); set<int> s; int res = 0; for (const auto &i: a) { b[i]--; if (b[i] == 0) t++; s.insert(i); if (s.size() == cnt) res++, s.clear(), cnt -= t, t = 0; } cout << res << "\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int TC; for (cin >> TC; TC; TC--) solve(); return 0; }

7|0I-Tokitsukaze and Short Path (plus)


容易推得绕路一定不优,所以就是所有边之和就是答案,因此第一个绝对值就能去掉,只剩下第二个绝对值。我们排序,然后维护前缀和后缀和即可。

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using i128 = __int128; using ldb = long double; #define int i64 using vi = vector<int>; using pii = pair<int, int>; using vii = vector<pii>; const int inf = INT_MAX, INF = LLONG_MAX; const int mod = 998244353; const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0}; using edge = array<int, 3>; void solve() { int n; cin >> n; vi a(n); int res = 0, sum = 0; for (auto &i: a) cin >> i, sum += i; res += sum * (n - 1) *2; sort(a.begin(), a.end()); for (int i = 0, x = 0, y = n, pre = 0, suf = sum; i < n; i++) { res += (x * a[i] - pre) + (suf - y * a[i]); x++, y--, pre += a[i], suf -= a[i]; } cout << res << "\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int TC; for (cin >> TC; TC; TC--) solve(); return 0; }

8|0J-Tokitsukaze and Short Path (minus)


通过简单的分类讨论就可以推得wu,v=2min(au,av)。因此最短路有两种情况,一种是直接走,另一种是绕路的最小值。

因此我们可以把所有的点排序,然后考虑从当前点出发是直接走短还是绕路短,并且计算出来回的方案数即可

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using i128 = __int128; using ldb = long double; #define int i64 using vi = vector<int>; using pii = pair<int, int>; using vii = vector<pii>; const int inf = INT_MAX, INF = LLONG_MAX; const int mod = 998244353; const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0}; void solve() { int n; cin >> n; vi a(n); int res = 0; for (auto &i: a) cin >> i; sort(a.begin(), a.end()); int d = a.front(), t = n; for (auto &i: a) { t--; res += min(4 * d, i * 2) * t * 2; } cout << res << "\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int TC; for (cin >> TC; TC; TC--) solve(); return 0; }

9|0K-Tokitsukaze and Password (easy)


判断一个数是不是8的倍数,只需要判断最低三位即可。

所以可以先爆搜出8的倍数,然后再爆搜其他位置即可

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using i128 = __int128; using ldb = long double; #define int i64 using vi = vector<int>; using pii = pair<int, int>; using vii = vector<pii>; const int inf = INT_MAX, INF = 1e18; const int mod = 998244353; const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0}; int n, y, res; set<int> vis; void dfs(string s, i32 i) { if (i == 3) { int x = 0; for (int j = 0, t = 1; j < 3; j++, t *= 10) x += (s[j] - '0') * t; if (x % 8 != 0) return; } if (i == n) { if (s.back() == '0' and n > 1) return; int x = 0; for (int j = 0, t = 1; j < n; j++, t *= 10) x += (s[j] - '0') * t; if (x % 8 != 0 or x > y) return; res++; return; } if (s[i] >= '0' and s[i] <= '9') { dfs(s, i + 1); return; } else if (s[i] == '_') { for (s[i] = '0'; s[i] <= '9'; s[i]++) dfs(s, i + 1); s[i] = '_'; return; } else { for (int j = '0'; j <= '9'; j++) { if (vis.count(j)) continue; vis.insert(j); auto t = s; for (auto &l: t) if (l == s[i]) l = j; dfs(t, i + 1); vis.erase(j); } } } void solve() { string s; cin >> n >> s >> y, res = 0; reverse(s.begin(), s.end()), vis.clear(); dfs(s, 0); cout << res << "\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int TC; for (cin >> TC; TC; TC--) solve(); return 0; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/18057127.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示