2025牛客寒假算法基础集训营1
2025牛客寒假算法基础集训营1
A-茕茕孑立之影_2025牛客寒假算法基础集训营1 (nowcoder.com)
思路
因为 \(a_i \le 10^9\),所以只要找一个大于 \(10^9\) 的质数即可,注意如果有 \(1\) 则无解。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n; cin >> n; bool ok = 1; vector<int> a(n); for (int i = 0; i < n; i ++) { cin >> a[i]; if (a[i] == 1) { ok = 0; } } if (!ok) { cout << "-1\n"; return; } cout << 1000000007 << "\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
B-一气贯通之刃_2025牛客寒假算法基础集训营1 (nowcoder.com)
思路
经过所有点一次且不重复,实际上就是找哈密顿路径,不过题目中说了是一棵树,那么只有这棵树是一条链的时候才存在,所以判断度数即可。
代码
#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; vector<int> in(n + 1); for (int i = 1; i < n; i ++) { int x, y; cin >> x >> y; in[x] ++, in[y] ++; if(in[x] > 2 || in[y] > 2){ cout << "-1\n"; return 0; } } vector<int> a; for(int i = 1;i <= n;i ++){ if(in[i] == 1){ a.emplace_back(i); } } cout << a[0] << " " << a.back() << "\n"; return 0; }
C-兢兢业业之移_2025牛客寒假算法基础集训营1 (nowcoder.com)
思路
参考嘤嘤嘤佬1
我们需要把所有的 \(1\) 都推到左上角,那么可以先把所有的 $1 $ 往最上方推,再把所有的 \(1\) 往最左边推,此时所有的 $1 $ 就会变成一个类似上三角矩阵的阶梯。
接下来我们就应该将右边多出的 \(1\) 和下边多出的 \(1\) 放进左上角,从边缘的 \(1\) 开始,往中间靠即可。
操作次数大概是:每个 1 都会从最底下移动到最上方,从最右边移动到最左边,次数是 \(2n\) ,再从边角往中间靠又需要 \(2n\) ,共 \(4n\) ,$ 1$ 的个数为 \(\frac {n^2} 4\) ,乘起来就是 \(n^3\) 。
理论次数超过了题目的要求,但实际上不可能每一个 1 都要走这么多次,就比如有一个 \(1\) 从右下角走到左上角走了 \(2n\) 步,必然会使得其他的 \(1\) 不需要走 \(2n\) 步,每个 \(1\) 走的步数都是不独立的,因此实际次数不会超过题目的要求。
时间复杂度 \(O(n^4)\)。
代码
#include<bits/stdc++.h> using namespace std; int main(){ int T = 1; cin >> T; while(T--){ int n; cin >> n; vector s(n + 2, string(n + 2, '1')); for(int i = 1; i <= n; i++){ cin >> s[i]; s[i] = "1" + s[i] + "1"; } vector<vector<int>> ans; auto go = [&](int x, int y, char c){ int dx = 0; dx -= c == 'U'; dx += c == 'D'; dx += x; int dy = 0; dy -= c == 'L'; dy += c == 'R'; dy += y; swap(s[x][y],s[dx][dy]); ans.push_back({x,y,dx,dy}); }; for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ for(int k = i; k >= 1; k--){ if(s[k][j] == '1' && s[k - 1][j] == '0'){ go(k, j, 'U'); } else break; } } } for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ for(int k = j; k >= 1; k--){ if(s[i][k] == '1' && s[i][k - 1] == '0'){ go(i, k, 'L'); } else break; } } } for(int i = n / 2; i >= 1; i--){ for(int j = n; j > n / 2; j--){ if(s[i][j] == '0') continue; int x = 0, y = 0; for(int k = 1; k <= n / 2; k++){ for(int l = 1; l <= n / 2; l++){ if(s[k][l] == '1') continue; x = k; y = l; break; } } for(int k = i; k < x; k++){ go(k, j, 'D'); } for(int k = j; k > y; k--){ go(x, k, 'L'); } } } for(int i = n; i > n / 2; i--){ for(int j = n / 2; j >= 1; j--){ if(s[i][j] == '0') continue; int x = 0, y = 0; for(int k = 1; k <= n / 2; k++){ for(int l = 1; l <= n / 2; l++){ if(s[k][l] == '1') continue; x = k; y = l; break; } } for(int k = j; k < y; k++){ go(i, k, 'R'); } for(int k = i; k > x; k--){ go(k, y, 'U'); } } } cout << ans.size() << endl; for(auto &i : ans){ for(auto &j : i){ cout << j << " "; } cout << endl; } } }
D-双生双宿之决_2025牛客寒假算法基础集训营1 (nowcoder.com)
思路
按照题目要求实现即可。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n; cin >> n; set<int> s; vector<int> a(n); for (int i = 0; i < n; i ++) { cin >> a[i]; s.insert(a[i]); } if ((n & 1) || s.size() != 2) { cout << "No\n"; return; } int x = count(a.begin(), a.end(), *s.begin()); int y = count(a.begin(), a.end(), *next(s.begin())); cout << (x == y ? "Yes\n" : "No\n"); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
E-双生双宿之错_2025牛客寒假算法基础集训营1 (nowcoder.com)
思路
参考嘤嘤嘤佬1
假设我们确定了双生数组的两种元素分别为 \(x\) 和 \(y\),令 \(x < y\)。
那么如果数组是有序的,我们显然应该将前一半变成 \(x\),后一半变成 \(y\),操作次数为 \(\sum_{i=1}^{n/2} |a_i - x| + \sum_{i=n/2+1}^{n} |a_i - y|\)。
如何求出确定 \(x\) 和 \(y\) 可以使得操作次数最小呢?
观察上述式子,前一半和后一半计算权值时是独立的,我们可以把这个数组分成左右两个部分,分别求值。
现在问题变成了:将一个数组的所有数都变成 \(t\) 的最小操作次数。
一个比较显然的结论是:取 \(t\) 为这个数组的中位数。
不会证明,感性理解一下:如果所有数字都为变成了 \(t\),现在要使得所有数字变成 \(t + 1\),需要让原本小于等于 \(t\) 的数字都加一次(记个数为 \(l\)),原本大于 \(t\) 的数字少加一次(记个数为 \(r\)),代价是 \(l - r\),其中 \(l + r = n\)。那么 \(t\) 最小值开始从小往上加的时候,\(l\) 会在 \(t\) 为中位数的时候大于等于 \(r\),代价就变成了非负整数。
如果不知道上述中位数结论或者推导的话,这个函数是满足三分性的,也可以使用三分暴力计算。
我们左右两边分别会心的结果可能会使得 \(x = y\),因此我们需要让 \(x - 1\) 或者 \(y + 1\),最后取其中的较小值。
时间复杂度 \(O(n\log n)\)。
代码
#include<bits/stdc++.h> using namespace std; int main(){ int T = 1; cin >> T; while(T--){ int n; cin >> n; vector<int> a(n + 1); for(int i = 1; i <= n; i++){ cin >> a[i]; } ranges::sort(a); auto get = [&](auto x, auto y){ auto ans = 0ll; for(int i = 1; i <= n / 2; i++){ ans += abs(a[i] - x); } for(int i = n / 2 + 1; i <= n; i++){ ans += abs(a[i] - y); } return ans; }; auto check = [&](int x, int y){ auto t = 0ll; if(a[x] != a[y]) t = get(a[x], a[y]); else t = min(get(a[x] - 1, a[y]), get(a[x], a[y] + 1)); return t; }; int t = n >> 1; int x = 0, y = 0; if(t & 1){ x = t + 1 >> 1; y = t + x; } else{ x = t >> 1; y = t + x + 1; } cout << check(x, y) << endl; } }
G-井然有序之衡_2025牛客寒假算法基础集训营1 (nowcoder.com)
思路
先判断 \(\sum\limits_{i=1}^na_i\) 是否等于 \(\sum\limits_{i=1}^ni\) ,满足的话,就排序之后,把 \(a_i < i\) 的部分累加起来就是答案。
代码
#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; i64 sum = 0; vector<int> a(n + 1); for (int i = 1; i <= n; i ++) { cin >> a[i]; sum += a[i]; } if (sum != 1LL * n * (n + 1) / 2) { cout << "-1\n"; return 0; } sort(a.begin() + 1, a.end()); i64 x = 0, y = 0; for (int i = 1; i <= n; i ++) { if (a[i] < i) { x += i - a[i]; } } cout << x << "\n"; return 0; }
H-井然有序之窗_2025牛客寒假算法基础集训营1 (nowcoder.com)
思路
首先将所有区间按照右端点从小到大排序,如果右端点相同,则按照左端点从大到小排序。这样做的目的是优先处理右端点较小的区间,确保每个区间都能找到合适的数。
使用一个集合 has
来存储所有可用的数(从 1 到 n)。对于每个区间,使用 lower_bound
在集合中找到第一个大于等于左端点的数。如果这个数在区间范围内,则选择这个数,并从集合中移除;否则,输出 -1
表示无解。
代码
#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; set<int> has; vector<array<int, 3>> lr(n); for (int i = 0; i < n; i ++) { cin >> lr[i][0] >> lr[i][1]; lr[i][2] = i + 1; has.insert(i + 1); } sort(lr.begin(), lr.end(), [](auto x, auto y) { if (x[1] != y[1]) return x[1] < y[1]; return x[0] > y[0]; }); vector<int> ans(n + 1); for (auto &[l, r, i] : lr) { auto t = has.lower_bound(l); if (t != has.end() && *t <= r) { ans[i] = *t; has.erase(t); } else { cout << "-1\n"; return 0; } } for (int i = 1; i <= n; i ++) { cout << ans[i] << " \n"[i == n]; } return 0; }
J-硝基甲苯之袭_2025牛客寒假算法基础集训营1 (nowcoder.com)
思路
题目要求统计数组中满足 \(a_i \oplus a_j = \gcd(a_i, a_j)\) 的数对 \((i, j)\)(其中 \(i < j\))的数量。关键在于将条件转化为数学关系:设 \(d = \gcd(a_i, a_j)\),则 \(a_i \oplus a_j = d\)。令 \(x = \frac{a_i}{d}\),\(y = \frac{a_j}{d}\),则 \(x \oplus y = 1\) 且 \(\gcd(x, y) = 1\)。由此可知,\(x\) 和 \(y\) 必须互质且二进制表示仅相差最低位。
预处理每个数的所有因数 \(d\)。对于当前数 \(a_i\),遍历其因数 \(d\),计算 \(y = a_i \oplus d\),若 \(\gcd(a_i, y) = d\),则累加此前已出现过的 \(y\) 的数量。最后将 \(a_i\) 加入统计容器。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; constexpr int N = 2e5 + 1; unordered_map<int, int> cnt; vector xd(N, vector<int>()); int main() { ios::sync_with_stdio(false); cin.tie(nullptr); for (int i = 1; i < N; i ++) { for (int j = i; j < N; j += i) { xd[j].emplace_back(i); } } int n; cin >> n; vector<int> a(n); for (auto &x : a) { cin >> x; } i64 ans = 0; for (auto &x : a) { for (auto &d : xd[x]) { int y = x ^ d; if (gcd(x, y) == d) { ans += cnt[y]; } } cnt[x] ++; } cout << ans << "\n"; return 0; }
M-数值膨胀之美_2025牛客寒假算法基础集训营1 (nowcoder.com)
思路
要最小化数组在恰好一次区间乘2操作后的极差,核心策略是处理最小值所在区间。设原数组最大值为\(mx\),最小值为\(mi\)。首先定位所有\(mi\)的最左\(l\)和最右\(r\)形成区间\([l, r]\)。
考虑两种情况:1) 仅将最左的\(mi\)(即\(a_l\))乘以2,此时新极差为\(\max(mx, 2mi) - \min(\text{其他元素})\);2) 将整个区间\([l, r]\)内所有元素乘以2,新极差为\(\max(\text{新数组元素}) - \min(\text{新数组元素})\)。最终取两种情况的极小值。
代码
#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; int mx = 0, mi = 1e9; vector<int> a(n + 1); for (int i = 1; i <= n; i ++) { cin >> a[i]; mx = max(mx, a[i]); mi = min(mi, a[i]); } int l = -1, r = -1; for (int i = 1; i <= n; i ++) { if (mi == a[i]) { if (l == -1) { l = i; } r = i; } } mx = max(mx, mi * 2); a[l] *= 2; mi = *min_element(a.begin() + 1, a.end()); int ans = mx - mi; for (int i = l + 1; i <= r; i ++) { a[i] *= 2; } mx = *max_element(a.begin() + 1, a.end()); mi = *min_element(a.begin() + 1, a.end()); ans = min(ans, mx - mi); cout << ans << "\n"; return 0; }
参考文献
本文作者:Ke_scholar
本文链接:https://www.cnblogs.com/Kescholar/p/18692141
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步