2024 湖南省赛(HNCPC 2024)

1|0C - easy math


Πai2024blog2(Π2ki)log2(2024b)kiblog22024

因此答案就是b=kilog22024

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui32 = unsigned int; #define int i64 using pii = pair<int,int>; using vi = vector<int>; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n; cin >> n; int sum = 0; for(int i = 0, x; i < n; i ++) { cin >> x; sum += log2(x); } cout << ceil(sum / log2(2024)); return 0; }

2|0E - 拼接串


考虑值域只有18,因此我们可以用f[i]表示状态i对应的最长区间。其中i是一个18位二进制整数

我们可以先用双指针统计出每个l对应的合法区间的最大右端点r。并且更新f

然后我们可以在做一次dp,状态为g[i]表示i的子集对应的最场区间。

此时答案就可以表示为max(g[i]+g[(2181)i])

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui32 = unsigned int; #define int i64 using pii = pair<int,int>; using vi = vector<int>; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n; cin >> n; vi a(n + 1); for(int i = 1; i <= n; i ++) cin >> a[i], a[i] = (1 << (a[i] - 1)); int N = (1 << 18) - 1; vi f(N + 1); for(int l = 1, r = 0, t = 0; l <= n; l ++) { while(r + 1<= n and (t & a[r + 1]) == 0) r ++, t |= a[r]; f[t] = max(f[t], r - l + 1); t ^= a[l]; } for(int i = 0; i < N; i ++) { for(int j = 1; j <= N; j <<= 1){ if((i & j) == 0) f[i | j] = max(f[i | j], f[i]); } } int res = 0; for(int i = 0; i <= N; i ++) res = max(res, f[i] + f[N ^ i]); cout << res; return 0; }

3|0H. 经文


f[i][j][l]表示前i位,且已经匹配了j个完整的串,当前串匹配到了l的方案数。然后我们可以枚举下一位放什么,这里面如果失配,下一位不一定需要从头开始匹配,我们可以用kmp的前缀数组进行计算下一位可能匹配到哪一位。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui32 = unsigned int; #define int i64 using pii = pair<int,int>; using vi = vector<int>; vector<int> prefix_function(const string &s) { vector<int> pi(s.size()); for (int i = 2, j = 0; i < s.size(); i++) { while (j > 0 && s[i] != s[j + 1]) j = pi[j]; if (s[i] == s[j + 1]) j++; pi[i] = j; } return pi; } const int mod = 998244353; struct mint { int x; mint(int x = 0) : x(x) {} int val() { return x = (x % mod + mod) % mod; } mint &operator=(int o) { return x = o, *this; } mint &operator+=(mint o) { return (x += o.x) >= mod && (x -= mod), *this; } mint &operator-=(mint o) { return (x -= o.x) < 0 && (x += mod), *this; } mint &operator*=(mint o) { return x = (i64) x * o.x % mod, *this; } mint &operator^=(int b) { mint w = *this; mint ret(1); for (; b; b >>= 1, w *= w) if (b & 1) ret *= w; return x = ret.x, *this; } mint &operator/=(mint o) { return *this *= (o ^= (mod - 2)); } friend mint operator+(mint a, mint b) { return a += b; } friend mint operator-(mint a, mint b) { return a -= b; } friend mint operator*(mint a, mint b) { return a *= b; } friend mint operator/(mint a, mint b) { return a /= b; } friend mint operator^(mint a, int b) { return a ^= b; } }; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n, k; cin >> n >> k; string s; cin >> s; int m = s.size(); s = " " + s; auto pi = prefix_function(s); vector f(n + 1, vector(k + 2, vector<mint>(m + 1))); f[0][0][0] = 1; for(int i = 0; i < n; i ++) { for(int j = 0; j <= k; j ++) { for(int l = 0; l < m; l ++){ for(char c = 'a'; c <= 'z'; c ++) { // 枚举 i+1 位放什么字母 bool ok = false;// 有没有匹配成功过 for(int pre = l; ok == false; pre = pi[pre]) { if(s[pre + 1] == c) { // 匹配 if(pre == m - 1){ f[i + 1][j + 1][0] += f[i][j][l]; } else { f[i + 1][j][pre + 1] += f[i][j][l]; } ok = true; } if(pre == 0) break; } if(ok) continue; f[i + 1][j][0] += f[i][j][l]; } } } } mint res = 0; for(int i = 0; i < m; i ++) res += f[n][k][i]; cout << res.val(); return 0; }

4|0I - 数据检索系统


按照题目模拟

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui32 = unsigned int; #define int i64 using pii = pair<int,int>; using vi = vector<int>; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int mod, k, n, q; cin >> mod >> k >> n >> q; vi vis(mod); for(int x; n; n --) { cin >> x; for(int i = 1, y = 1; i <= k; i ++) { y = y * x % mod; vis[y] |= 1; } } for(int x, f; q; q --) { cin >> x, f = 1; for(int i = 1, y = 1; i <= k; i ++) { y = y * x % mod; f &= vis[y]; } cout << f << " "; } return 0; }

5|0J - Beautiful Sequence


对于数组a[i]我们记A表示数字a[i]出现在了下标A[a[i]]的位置。对于b有类似的B

对于题目的要求,可以转换为对于值域[l,r],如果值域内的数都选就是 beautifu 序列。

很容想到一个性质,对于值域[l,r]的子序列,如果a,b相同,则一定满足值域[l,r1]的子序列a,b也一定相同。

因此我们可以用双指针求出对于每一个l符合条件的最大的r

现在我们考虑如何快速判断出值域[l,r]的子序列在a,b出现的顺序相同。此时应满足

x,y[l,r],(A[x]>A[y])=(B[x]>B[y])

我们如果暴力的判断实际是O(N2)的,加上双指针的复杂度,肯定无法通过。

我们可以考虑,如何在已知[l,r1]满足的情况下,判断[l,r]是否满足?

我们可以用两个std::set<int>分别维护A[l,r1],B[l,r1]。 当我们插入A[r],B[r],可以在set中找到前后的第一个数字,并判断是否相等,这样的复杂度是O(logN)。所以总体复杂度就是O(NlogN)

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui32 = unsigned int; #define int i64 using pii = pair<int,int>; using vi = vector<int>; const int inf = LLONG_MAX / 2; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n, m; cin >> n; vi a(n + 1), b(n + 1), A(n + 1), B(n + 1); for(int i = 1; i <= n; i ++){ cin >> a[i]; A[a[i]] = i; } for(int i = 1; i <= n; i ++) { cin >> b[i]; B[b[i]] = i; } int res = 0; set<int> pa , pb; for(int l = 1, r = 0; l <= n; l ++ ){ auto check = [&](int i) -> bool { int x = A[i], y = B[i]; auto rx = pa.upper_bound(x), ry = pb.upper_bound(y); if((rx == pa.end()) != (ry == pb.end())) return false; if(rx != pa.end()) { if(a[*rx] != b[*ry]) return false; } if((rx == pa.begin()) != (ry == pb.begin())) return false; if(rx == pa.begin()) return true; auto lx = prev(rx), ly = prev(ry); if(a[*lx] != b[*ly]) return false; return true; }; while(r + 1 <= n and check(r + 1)){ r ++; pa.insert(A[r]), pb.insert(B[r]); } res += r - l + 1; pa.erase(A[l]), pb.erase(B[l]); } cout << res; }

6|0K - 渡劫


我们可以建立分层图,这样的话一次免费的机会就是层与层之间的单向边。然后建立一个超级终点,然后从每一个点到超级终端的单向边权就是点权。

然后就是要求所有点到终点的最短路,实际上我们从终点反向建边求单源最短路。

答案就是最短路的最大值。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui32 = unsigned int; #define int i64 using pii = pair<int,int>; using vi = vector<int>; const int inf = LLONG_MAX / 2; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n, m; cin >> n >> m; int N = 2 * n; vector<vector<pii>> e(N + 1); for(int u, v, w; m; m --) { cin >> u >> v >> w; e[u].emplace_back(v, w); e[v].emplace_back(u, w); e[u].emplace_back(v + n, 0); e[v].emplace_back(u + n, 0); e[u + n].emplace_back(v + n, w); e[v + n].emplace_back(u + n, w); } for(int i = 1, x; i <= n; i ++) { cin >> x; e[0].emplace_back(i, x); e[0].emplace_back(i + n, x); } vi dis(N + 1, inf), vis(N + 1); dis[0] = 0; priority_queue<pii,vector<pii>, greater<>> heap; heap.emplace(0, 0); while(not heap.empty()) { auto [d, u] = heap.top(); heap.pop(); if(vis[u]) continue; vis[u] = 1; for(auto [v, w] : e[u]) { if(vis[v] or dis[v] <= d + w) continue; dis[v] = d + w; heap.emplace(dis[v], v); } } int res = -1; for(int i = 1; i <= n; i ++) res = max(res, min(dis[i], dis[i + n])); cout << res; return 0; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/18538584.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(88)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
历史上的今天:
2023-11-10 The 10th Jimei University Programming Contest
2022-11-10 牛客小白月赛 54
2022-11-10 牛客小白月赛55
2021-11-10 2021 ccpc 女生赛部分题解
点击右上角即可分享
微信分享提示