首页 > 编程语言 >2025牛客寒假算法基础集训营1

2025牛客寒假算法基础集训营1

时间:2025-01-21 19:43:22浏览次数:1  
标签:std 2025 int ++ cin 牛客 数组 ans 集训营


A. 茕茕孑立之影

题意:给你\(n\)个数,你要找一个数使得这个数和数组的任意一个数都不成倍数关系。

如果数组里有\(1\)肯定不行,\(1\)是所有数的因子。其他情况我们只需要找一个大质数就行,因为值域只有\(1e9\),可以输出\(1e9+7\)。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n);
	int ans = 1e9 + 7;
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	std::sort(a.begin(), a.end());
	if (a[0] == 1) {
		std::cout << -1 << "\n";
	} else {
		std::cout << ans << "\n";
	}
}

B. 一气贯通之刃

题意:给你一颗树,要你找一条简单路径经过所有点。

如果这颗树不是一条链的话,不可能找到一条路径经过所有点。所以判断是不是链,然后找链的两端就行。
可以用度数判断,度数为一个点连的边数量。一条链上的点度数都小于等于\(2\),并且两端的点度数是\(1\)。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<int> deg(n);
	for (int i = 1; i < n; ++ i) {
		int u, v;
		std::cin >> u >> v;
		-- u, -- v;
		++ deg[u];
		++ deg[v];
	}

	std::vector<int> ans;
	int cnt = 0;
	for (int i = 0; i < n; ++ i) {
		if (deg[i] == 1) {
			ans.push_back(i);
		}

		cnt += deg[i] == 2;
	}

	if (cnt != n - 2 || ans.size() != 2) {
		std::cout << -1 << "\n";
	} else {
		std::cout << ans[0] + 1 << " " << ans[1] + 1 << "\n";
	}
}

C. 兢兢业业之移

题意:给你一个\(01\)矩阵,你要把所有的\(1\)都移动到左上部分。给出方案。

直接枚举所有左上部分的点,我们按行从上到下,按列从左到右枚举。那么如果我们枚举到了\((i,j)\),则所有\(1 <=x < i, 1 <= y <= n / 2\)的地方以及\(x=i, 1 <= y < j\)的地方全都是\(1\),如果这个\((i, j)\)是\(0\),我们找一个不在已经操作好的位置的\(1\)的位置移过来就行了。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<std::string> s(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> s[i];
	}

	std::vector<std::array<int, 4> > ans;
	for (int i = 0; i < n / 2; ++ i) {
		for (int j = 0; j < n / 2; ++ j) {
			if (s[i][j] == '0') {
				int x = -1, y = -1;
				for (int l = 0; l < n && x == -1; ++ l) {
					for (int r = 0; r < n && x == -1; ++ r) {
						if ((l < i && r < n / 2) || (l == i && r < j)) {
							continue;
						}
						if (s[l][r] == '1') {
							x = l, y = r;
							break;
						}
					}
				}

				while (x < i) {
					std::swap(s[x][y], s[x + 1][y]);
					ans.push_back({x, y, x + 1, y});
					++ x;
				}

				while (y < j) {
					std::swap(s[x][y], s[x][y + 1]);
					ans.push_back({x, y, x, y + 1});
					++ y;
				}

				while (y > j) {
					std::swap(s[x][y], s[x][y - 1]);
					ans.push_back({x, y, x, y - 1});
					-- y;
				}

				while (x > i) {
					std::swap(s[x][y], s[x - 1][y]);
					ans.push_back({x, y, x - 1, y});
					-- x;
				}
			}
		}
	}

	std::cout << ans.size() << "\n";
	for (auto & [a, b, c, d] : ans) {
		std::cout << a + 1 << " " << b + 1 << " " << c + 1 << " " << d + 1 << "\n";
	}
}

D. 双生双宿之决

题意:一个数组是双生数组,那么它恰好有两种元素,并且每一种元素的个数是\(n/2\)。判断给你的数组是不是双生数组。

统计判断即可。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::map<int, int> mp;
	for (int i = 0; i < n; ++ i) {
		int x;
		std::cin >> x;
		++ mp[x];
	}

	std::vector<int> a;
	for (auto & [x, y] : mp) {
		a.push_back(y);
	}

	if (a.size() != 2 || a[0] != a[1]) {
		std::cout << "No\n";
	} else {
		std::cout << "Yes\n";
	}
}

E. 双生双宿之错

题意:一个数组是双生数组,那么它恰好有两种元素,并且每一种元素的个数是\(n/2\),你每次可以让数组一个元素加一或者减一,求让数组变成双生数组的最小操作数。

先排序,因为只有两个数组并且每个都是一半,那么我们分成左半和右半来操作。每一边都要变成一个相同的数。变成两边的中位数是最优的。但可能两边中位数一样,那么我们枚举两边中位数减少或增大就行了。
为什么中位数最优?具体的来说,我们设在中间位置左边的所有点,到中位数的差之和为\(p\),右边的差之和则为\(q\)那么我们就必须让\(p+q\)的值尽量小。当位置向左移动的话,\(p\)会减少\(x\)但是\(q\)会增加\(n−x\).所以说当为数组中位数的时候,\(p+q\)最小。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<i64> a(n + 1);
	for (int i = 1; i <= n; ++ i) {
		std::cin >> a[i];
	}

	if (n == 2) {
		if (a[1] == a[2]) {
			std::cout << 1 << "\n";
		} else {
			std::cout << 0 << "\n";
		}
		return;
	}

	std::sort(a.begin(), a.end());
	if (a[1] == a[n]) {
		std::cout << n / 2 << "\n";
		return;
	}

	i64 midl = a[n / 2 / 2], midr = a[n / 2 + n / 2 / 2];
	i64 ans = 1e18;
	for (i64 x = midl - 10; x <= midl + 10; ++ x) {
		for (i64 y = midr - 10; y <= midr + 10; ++ y) {
			if (x == y) {
				continue;
			}
			i64 sum = 0;
			for (int i = 1; i <= n; ++ i) {
				if (i <= n / 2) {
					sum += std::abs(a[i] - x);
				} else {
					sum += std::abs(a[i] - y);
				}
			}

			ans = std::min(ans, sum);
		}
	}

	midl = a[n / 2 / 2 + 1];
	midr = a[n / 2 + n / 2 / 2 + 1];
	for (i64 x = midl - 10; x <= midl + 10; ++ x) {
		for (i64 y = midr - 10; y <= midr + 10; ++ y) {
			if (x == y) {
				continue;
			}
			i64 sum = 0;
			for (int i = 1; i <= n; ++ i) {
				if (i <= n / 2) {
					sum += std::abs(a[i] - x);
				} else {
					sum += std::abs(a[i] - y);
				}
			}

			ans = std::min(ans, sum);
		}
	}

	std::cout << ans << "\n";
}

F. 双生双宿之探

题意:双生数组的定义和\(E\)题一样。求有多少子数组是双生数组。

考虑双指针枚举每个只有两个数的区间。那么我们确定了双生数组的两个数。那么就可以单独求这个区间的贡献。设两个数是\(x,y\),当\(a_i\)等于\(x\)时加\(1\),否则减\(1\)。那么就变成求一个前缀和和当前相等。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	i64 ans = 0;
	for (int i = 0; i < n; ++ i) {
		int j = i + 1;
		std::map<int, int> mp;
		++ mp[a[i]];
		int x = a[i], y;
		while (j < n) {
			mp[a[j]] ++ ;
			if (mp.size() > 2) {
				break;
			}

			if (a[j] != x) {
				y = a[j];
			}

			++ j;
		}

		if (mp.size() == 1) {
			break;
		}

		std::map<int, int> cnt;
		++ cnt[0];
		int sum = 0;
		for (int k = i; k < j; ++ k) {
			if (a[k] == x) {
				++ sum;
			} else {
				-- sum;
			}

			ans += cnt[sum];
			++ cnt[sum];
		}

		for (int k = j - 1; k >= i; -- k) {
			if (a[k] != a[j - 1]) {
				i = k;
				break;
			}
		}
	}

	std::cout << ans << "\n";
}

G. 井然有序之衡

题意:给你一个数组,你每次可以给一个数加一同时给另一个数减一。问能不能变成一个排列,求最小操作数。

将数组排序后,那么应该时最小的变成\(1\),第二小的变成\(2\) ... 最大的变成\(n\)。那么模拟即可。
因为每次操作不会改变数组总和,那么一个排列的总和位\(\frac{n(n+1)}{2}\),判断数组总和是不是这个数就行了。不是则不可能变成。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	if (std::accumulate(a.begin(), a.end(), 0ll) != (i64)n * (n + 1) / 2) {
		std::cout << -1 << "\n";
		return;
	}

	std::sort(a.begin(), a.end());

	i64 ans = 0;
	for (int i = 0, j = n - 1; i < j;) {
		if (a[i] == i + 1) {
			++ i;
		} else if (a[j] == j + 1) {
			-- j;
		} else {
			i64 t = std::min(i + 1 - a[i], a[j] - (j + 1));
			ans += t;
			a[i] += t;
			a[j] -= t;
		}
	}
	std::cout << ans << "\n";
}

H. 井然有序之窗

题意:有一个排列,现在告诉你每个位置数的范围,你要构造一个合适的排列。

我们按照右端点从大到小给,每次尽量给最小的。因为如果有一个区间\(l\)更小并且长度小于当前区间但在它后面,那么他可选的数更多,我们不应该先关心它。否则在前面,已经考虑过了。用一个\(set\)维护没选过的数即可。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<std::array<int, 3> > a(n);
	for (int i = 0; i < n; ++ i) {
		int l, r;
		std::cin >> l >> r;
		a[i] = {r, l, i};
	}

	std::sort(a.begin(), a.end());
	std::vector<int> ans(n);

	std::set<int> s;
	for (int i = 1; i <= n; ++ i) {
		s.insert(i);
	}

	for (auto & [r, l, i] : a) {
		auto it = s.lower_bound(l);
		if (it == s.end() || *it > r) {
			std::cout << -1 << "\n";
			return;
		}

		ans[i] = *it;
		s.erase(it);
	}

	for (int i = 0; i < n; ++ i) {
		std::cout << ans[i] << " \n"[i == n - 1];
	}
}

J. 硝基甲苯之袭

题意:给你一个数组,有多少对\((i, j)\)满足\(gcd(a_i, a_j) = a_i \oplus a_j\)。

因为值域很小,那么我们枚举每个数的约数\(d\),然后判断\(gcd(a_i, a_i \oplus d)\)是不是等于\(d\)就行了。从左到右计算,记录前面每个数的数量。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	const int N = 2e5 + 5;
	std::vector<std::vector<int> > factor(N);
	for (int i = 1; i < N; ++ i) {
		for (int j = i; j < N; j += i) {
			factor[j].push_back(i);
		}
	}

	std::sort(a.begin(), a.end());
	std::map<int, int> cnt;
	i64 ans = 0;
	for (int i = 0; i < n; ++ i) {
		for (auto & j : factor[a[i]]) {
			if (std::gcd(a[i], a[i] ^ j) == j) {
				ans += cnt[a[i] ^ j];
			}
		}

		cnt[a[i]] += 1;
	}

	std::cout << ans << "\n";
}

M. 数值膨胀之美

题意:给你一个数组,你要恰好执行一次操作,选择一段区间让这个区间的数都乘2。最小化极差。

要让影响极差,那么我们肯定要改最大值和最小值,发现改最大值只会变大。那么我们应该操作最小值。随便找一个最小值的位置,然后两边扩展,看是不是最小值然后乘\(2\)即可。要用\(set\)实时维护最大最小值。

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<i64> a(n);
	std::multiset<i64> s;
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
		s.insert(a[i]);
	}

	i64 ans = 1e18;
	for (int i = 0; i < n; ++ i) {
		if (a[i] == *s.begin()) {
			s.extract(a[i]);
			s.insert(a[i] * 2);
			ans = std::min(ans, *s.rbegin() - *s.begin());
			int l = i - 1, r = i + 1;
			while (l >= 0 || r < n) {
				if (l >= 0 && a[l] == *s.begin()) {
					s.extract(a[l]);
					s.insert(a[l] * 2);
					ans = std::min(ans, *s.rbegin() - *s.begin());
					-- l;
				} else if (r < n && a[r] == *s.begin()) {
					s.extract(a[r]);
					s.insert(a[r] * 2);
					ans = std::min(ans, *s.rbegin() - *s.begin());
					++ r;
				} else {
					break;
				}
			}
			break;
		}
	}

	std::cout << ans << "\n";
}

标签:std,2025,int,++,cin,牛客,数组,ans,集训营
From: https://www.cnblogs.com/maburb/p/18684325

相关文章

  • 2025牛客寒假算法基础集训营1 ptlks的题解
    A.茕茕孑立之影题意:给定序列,找出一个数x,满足x和数组中任意一个元素都互不为倍数关系思路x范围为1e18以内,序列元素范围为1e9以内,选大于1e9的质数即可,特判序列中有1的情况。代码点击查看代码voidsolve(){ intn; cin>>n; intf=1; for(inti=1;i<=n;i++){ cin>>a[......
  • 2025年1月19日(什么是反馈系统)
    反馈系统是一种控制系统,其特点是系统的输出会被反馈到系统的输入端,以调节系统的行为和性能。在反馈系统中,系统的输出被测量并与期望的参考信号进行比较,然后根据比较结果来调整系统的输入,以使系统的输出接近期望值。反馈系统通常包括以下几个基本组成部分:传感器(Sensor):用于......
  • 【2025】Visual Studio详细安装使用教程(C/C++编译器)零基础入门到精通,收藏这一篇就够了
    Part1VisualStudio2022简介微软在官方开发博客中宣布,于2021年夏季发布VisualStudio2022的首个预览版,2022版本更快、更易于使用、更轻量级,专为学习者和构建工业规模解决方案的人设计。64位版的VisualStudio不再受内存限制困扰,主devenv.exe进程不再局限于4GB,用户......
  • 2025年3月全国计算等级考试(报名操作指南)从零基础到精通,收藏这篇就够了!
    2025年3月全国计算等级考试*报名指南新学期·新起点报名时间下次全国计算机等级考试时间:2025年3月22-23日。预计报名时间:2024年12月底至2025年1月上旬,各省具体报名时间可能有所差异,以正式通知为准。报名要求及步骤一般来说,无论你是大学生还是已经工作的职场人士,都是......
  • 2025 年 IPTV/APTV 直播源m3u(1月21日更新)
    前言注意:仅供学术学习研究使用。⚠️长期更新,建议收藏!更新日志源不在多,而在于精。0930将直播源做了较大更新,删除了大量不可用源地址。1月21日新增IPTV源:https://iptv.hacks.tools/1月21日新增直播源网站1月1日更新确认源可用性。1016新......
  • 【SD零基础教程】Stable Diffusion如何图生图?2025最新SD保姆级教学,新手建议收藏!
    今天想要跟大家分享的是如何利用StableDiffusion图生图,图生图说白了就是根据已有的一张图片给它变化成不同的风格,三次元图片变成二次元图片,二次元变成三次元图片等等。那么具体该如何操作呢?跟着我一步步来吧。首先第一步我们需要有我们的开源软件StableDiffusion,在这里跟......
  • 2025毕设springboot 基于的热点新闻搜索系统论文+源码
    系统程序文件列表开题报告内容研究背景在信息爆炸的时代,新闻作为传递信息、反映社会动态的重要载体,其传播速度和覆盖范围日益扩大。随着互联网的普及和移动设备的广泛应用,人们获取新闻的方式已经从传统的报纸、电视逐渐转向在线平台。热点新闻作为公众关注的焦点,具有极高的......
  • 2025毕设springboot 基于的牵手沟通网站的设计与实现论文+源码
    系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,人与人之间的沟通交流方式正经历着前所未有的变革。在这个信息爆炸的时代,人们渴望找到更加便捷、高效且富有情感的沟通平台。传统的沟通方式,如面对面交流或电话沟通,虽然直接,但在时间和空间上存在一定的局限性......
  • 2025毕设springboot 基于的农产品批发服务系统论文+源码
    系统程序文件列表开题报告内容研究背景在农业现代化与信息化快速发展的背景下,农产品批发服务作为连接农业生产者与消费者的关键环节,其效率与透明度直接影响着农产品的流通速度与市场响应能力。传统的农产品批发模式往往依赖于实体市场,存在着信息不对称、交易成本高、物流效......
  • 2025年带你探索Trello、JIRA替代品:8款顶级项目管理工具全面解析!
    在项目管理的广阔领域中,Trello和JIRA一直是备受瞩目的工具,但随着市场的不断发展,还有许多优秀的替代品值得我们去探索。本文将为你详细解析8款顶级项目管理工具,它们分别是禅道、Trello、Asana、MicrosoftProject、Wrike、Monday.com、Basecamp、Teambition。这些工具各有千秋,涵盖......