2024牛客暑期多校训练营2
2024牛客暑期多校训练营2
E-GCD VS XOR_2024牛客暑期多校训练营2 (nowcoder.com)
题意
给定 x,构造 y < x 使得 gcd(x, y) = x ⊕ y
思路
取 x − lowbit(x) 即可,如果 x 是 2 的整数次幂则无解。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; void solve() { i64 n; cin >> n; i64 ans = n - (n & -n); cout << (ans ? ans : -1) << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) solve(); return 0; }
C-Red Walking on Grid_2024牛客暑期多校训练营2 (nowcoder.com)
题意
给定一个 2 行 n 列的地图,有一些格子为障碍
任选起点,每个格子最多只能走一次(不能走到障碍),求最长路径
思路
考虑 dp,从左往右遍历,遇到上下都是 \(R\) 的时候说明上下可转移,在上面的由下和左转移,在下面的由上和左转移,然后更新答案。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; string s[2]; cin >> s[0] >> s[1]; array<int, 2> dp{0, 0}; int ans = 0; for (int i = 0; i < n; i ++) { if (s[0][i] == 'R') dp[0] ++; else dp[0] = 0; if (s[1][i] == 'R') dp[1] ++; else dp[1] = 0; if (s[0][i] == 'R' && s[1][i] == 'R') { dp = {max(dp[0], dp[1] + 1), max({dp[1], dp[0] + 1})}; } ans = max({ans, dp[0], dp[1]}); } ans = max(ans - 1, 0); cout << ans << '\n'; return 0; }
H-Instructions Substring_2024牛客暑期多校训练营2 (nowcoder.com)
题意
平面直角坐标系中,小红初始站在原点
给定一个包含“上、下、左、右”的、长度为 n的指令序列,以及一个
特殊点 (x, y)
求选定一个子串,小红根据该子串序列移动,可以经过特殊点的方案数
思路
考虑前缀和记录每次到达的坐标,枚举左端点,对于从该左端点为起点第一次经过 (x,y) 的右端点,其右端点右边的点均为合法的点。
倒着枚举,找差分下标即可。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, x, y; cin >> n >> x >> y; string s; cin >> s; vector<array<int, 2>> a(n + 1); a[0] = {0, 0}; for (int i = 0; i < n; i ++) { a[i + 1] = a[i]; if (s[i] == 'W') { a[i + 1][1] ++; } else if (s[i] == 'S') { a[i + 1][1] --; } else if (s[i] == 'A') { a[i + 1][0]--; } else a[i + 1][0]++; } int ans = 0; map<array<int, 2>, int> idx; for (int i = n; i >= 0; i --) { idx[a[i]] = i; if (idx.count({a[i][0] + x, a[i][1] + y})) { int j = idx[ {a[i][0] + x, a[i][1] + y}]; j = max(j, i + 1); ans += n - j + 1; } } cout << ans << '\n'; return 0; }
B-MST_2024牛客暑期多校训练营2 (nowcoder.com)
题意
给定一个 n 个点的简单带权无向图 G
每次询问一个点集 S,求 S 关于 G 导出子图的最小生成树
没有输出 −1
思路
考虑根号分治。
因为没有办法使用邻接矩阵,所有首先用 map,将整个图存下来
考虑两种可能的暴力
设以 \(\sqrt n\) 为界。
对于 \(k\le \sqrt n\):对于给定点集 S,双重循环枚举 S 中的每个点,把其中需要的
边均取出来,并排序,使用 kruskal 算法,求出最小生成树。
对于 \(k>\sqrt n\) :对于给定点集 S,循环枚举 S 中的每个点,在循环枚举该点的
所有边,将需要的边取出来,并排序,使用 kruskal 算法,求出最小生成树。
因为边是双向边,边数组的大小得开 2 倍。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; const int N = 1e5 + 10; map<int, int> g[N]; vector<int> vis(N), node(N), fa(N); vector<array<int, 3>> edge(N << 1); int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m, q; cin >> n >> m >> q; for (int i = 0; i < m; i ++) { int u, v, w; cin >> u >> v >> w; g[u][v] = g[v][u] = w; } const int base = sqrt(n); auto find = [&](auto & self, int x)->int{ return fa[x] == x ? x : fa[x] = self(self, fa[x]); }; while (q--) { int k; cin >> k; for (int i = 0; i < k; i ++) { cin >> node[i]; int u = node[i]; fa[u] = u; vis[u] = 1; } int cnt = 0; if (k <= base) { for (int i = 0; i < k; i ++) for (int j = i + 1; j < k; j ++) if (g[node[i]].count(node[j])) edge[cnt++] = { g[node[i]][node[j]], node[i], node[j]}; } else { for (int i = 0; i < k; i ++) { int u = node[i]; for (auto &[v, w] : g[u]) if (vis[v]) edge[cnt++] = {w, u, v}; } } sort(edge.begin(), edge.begin() + cnt); i64 ans = 0, kuai = 0; for (int i = 0; i < cnt; i ++) { auto &[w, u, v] = edge[i]; u = find(find, u), v = find(find, v); if (u != v) { fa[u] = v; ans += w; kuai ++; } if (kuai == k - 1) break; } for (int i = 0; i < k; i ++) { int u = node[i]; fa[u] = u; vis[u] = 0; } if (kuai != k - 1) ans = -1; cout << ans << '\n'; } return 0; }
I-Red Playing Cards_2024牛客暑期多校训练营2 (nowcoder.com)
题意
给定一个长度为 2 × n 的数组,1 到 n 每个元素恰好出现两次
每次操作可以删除一个长度不小于 2 的连续子数组,需要满足该子数组
首尾相同,获得该连续子数组“首尾元素值”乘以“元素数量”的分数
问最终可以得到的最大分数
思路
处理出 \(1\sim n\) 每个数包含的区间,考虑从大到小去枚举每个区间产生的贡献,因为大数产生的贡献一定比小数产生的更多。
设 \(f_x\) 表示 x 这个数所包含的区间所产生的贡献。
遍历 x 所包含的区间,设 \(dp_i\) 表示当前 \(l_x+1\sim i\) 所产生的贡献,如果对于 \(a_i\) 这个数,其 \([l_{a_i},r_{a_i}] \subset [l_x,r_x]\) ,那么有 \(dp_{r_{a_i}+1} = \max(dp_{r_{a_i}+1},dp_i+f_{a_i})\),对于其他位置, x 产生的贡献就是 x,即 \(dp_{i+1}=\max(dp_i+x,dp_{i+1})\),开始在两端补 0 的话,最后的答案即就是 \(f_0\)。
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; n ++; vector<int> a(2 * n); for (int i = 1; i < 2 * n - 1; i ++) cin >> a[i]; vector<int> l(n, -1), r(n); for (int i = 0; i < 2 * n; i ++) { cin >> a[i]; if (l[a[i]] == -1) { l[a[i]] = i; } else { r[a[i]] = i; } } vector<int> f(n); for (int x = n - 1; x >= 0; x --) { vector<int> dp(2 * n + 1); for (int i = l[x] + 1; i < r[x]; i ++) { if (dp[i + 1] < dp[i] + x) dp[i + 1] = dp[i] + x; if (i == l[a[i]] && r[a[i]] < r[x]) { if (dp[r[a[i]] + 1] < dp[i] + f[a[i]]) dp[r[a[i]] + 1] = dp[i] + f[a[i]]; } } f[x] = 2 * x + dp[r[x]]; } cout << f[0] << '\n'; return 0; }
G-The Set of Squares_2024牛客暑期多校训练营2 (nowcoder.com)
题意
定义一个多重数集合是好的,但且仅当集合中元素乘积是一个正整数的
平方,集合的权值为这个正整数的值。
求大小为 N的多重数集的所有子集中所有好集的权值和
思路
注意到 \(\sqrt{1000}\) 以内的质数只有 {2, 3, 5, 7, 11, 13, 17, 19, 23, 27, 31} 一共 11 个
注意到对于 1000 以内的数,质因子中不可能出现两个大于 32 的质数
按照大质数分组,小质数状态压缩当成背包的体积,做分组背包
小质数作为第一组,其他大质数各自一组,转移时对于第 11 位上的 1 要记得清零
注意:数字 1 根据写法不同,可能要特殊处理,大质数配对、小质数配
对都需要计算贡献,分类讨论不要遗漏情况
代码
#include<bits/stdc++.h> using namespace std; using i64 = long long; constexpr i64 mod = 1e9 + 7, N = 1000; i64 prime[12] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; int m = 0; vector vec(N + 1, vector<array<int, 2>>()); for (int i = 0; i < n; i ++) { int x; cin >> x; m = max(x, m); int mask = 0, val = 1; for (int j = 0; j < 11; j ++) { while (x % prime[j] == 0) { x /= prime[j]; mask ^= 1 << j; if (!(mask >> j & 1)) { val = val * prime[j] % mod; } } } vec[x].push_back({mask, val}); } vector<i64> dp(1 << 12); dp[0] = 1; for (int i = 1; i <= m; i ++) { if (i * i <= 1000 && i != 1) continue; if (vec[i].empty()) continue; prime[11] = i; for (auto [st, val] : vec[i]) { auto ndp = dp; if (i != 1) st |= 1 << 11; for (int j = 0; j < (1 << 12); j ++) { int both = j & st, nval = val; for (int k = 0; k < 12; k ++) { if (both >> k & 1) { nval = nval * prime[k] % mod; } } ndp[j ^ st] += dp[j] * nval % mod; ndp[j ^ st] %= mod; } dp = move(ndp); } for (int j = 1 << 11; j < (1 << 12); j ++) dp[j] = 0; } cout << dp[0] - 1 << '\n'; return 0; }
本文作者:Ke_scholar
本文链接:https://www.cnblogs.com/Kescholar/p/18316958
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2023-07-22 第二次比赛出题题解