Welcome To Ke_scholar's Blog|

Ke_scholar

园龄:2年1个月粉丝:30关注:10

2025-01-26 21:09阅读: 23评论: 0推荐: 0

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;
}

参考文献

1. 牛客寒假营第一场题解讨论帖牛客网

本文作者:Ke_scholar

本文链接:https://www.cnblogs.com/Kescholar/p/18692141

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Ke_scholar  阅读(23)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起