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. 牛客寒假营第一场题解讨论帖牛客网

posted @ 2025-01-26 21:09  Ke_scholar  阅读(41)  评论(0)    收藏  举报