2025牛客寒假算法基础集训营1
A. 茕茕孑立之影
题意:给你
如果数组里有
点击查看代码
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. 一气贯通之刃
题意:给你一颗树,要你找一条简单路径经过所有点。
如果这颗树不是一条链的话,不可能找到一条路径经过所有点。所以判断是不是链,然后找链的两端就行。
可以用度数判断,度数为一个点连的边数量。一条链上的点度数都小于等于
点击查看代码
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. 兢兢业业之移
题意:给你一个
直接枚举所有左上部分的点,我们按行从上到下,按列从左到右枚举。那么如果我们枚举到了
点击查看代码
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. 双生双宿之决
题意:一个数组是双生数组,那么它恰好有两种元素,并且每一种元素的个数是
统计判断即可。
点击查看代码
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. 双生双宿之错
题意:一个数组是双生数组,那么它恰好有两种元素,并且每一种元素的个数是
先排序,因为只有两个数组并且每个都是一半,那么我们分成左半和右半来操作。每一边都要变成一个相同的数。变成两边的中位数是最优的。但可能两边中位数一样,那么我们枚举两边中位数减少或增大就行了。
为什么中位数最优?具体的来说,我们设在中间位置左边的所有点,到中位数的差之和为
点击查看代码
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. 双生双宿之探
题意:双生数组的定义和
考虑双指针枚举每个只有两个数的区间。那么我们确定了双生数组的两个数。那么就可以单独求这个区间的贡献。设两个数是
点击查看代码
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. 井然有序之衡
题意:给你一个数组,你每次可以给一个数加一同时给另一个数减一。问能不能变成一个排列,求最小操作数。
将数组排序后,那么应该时最小的变成
因为每次操作不会改变数组总和,那么一个排列的总和位
点击查看代码
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. 井然有序之窗
题意:有一个排列,现在告诉你每个位置数的范围,你要构造一个合适的排列。
我们按照右端点从大到小给,每次尽量给最小的。因为如果有一个区间
点击查看代码
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];
}
}
I. 井然有序之桠
题意:给你一个排列,你要构造一个排列,使得
这种构造题真的是靠智商吧。。。
看
首先我们不关注
我们知道,使用分治法的前提时我们可以把原问题分成多个和原问题等价的规模更小的问题,那么我们将问题改为求
这个思路似乎很好,但这个题还有两个特殊情况,如果不通过大量试样例或者打表找规律,我肯定是想不到的。如果我们遇到我们想出来一个很好的方法还是无法通过的情况,可以打表找特例。
这里,用下面的代码发现好像只有两个样例,如果没发现特例是有规律的,那就直接特判再交一发看看。
点击查看代码
for (int i = 1; i <= 5000; ++ i) {
for (int j = i; j < i + i - 1; ++ j) {
if (j - 2 > (i - 2) * (i - 1) / 2) {
std::cout << i << " " << j << "\n";
}
}
}
下面是参考
点击查看代码
std::vector<int> get(int n, i64 k) {
std::vector<int> p;
if (n == 0) {
} else if (n == 1) {
p = {1};
} else if (k >= n + n - 1) {
p = get(n - 1, k - n);
p.push_back(n);
} else if (n == 3 && k == 4) {
p = {3, 2, 1};
} else if (n == 4 && k == 6) {
p = {3, 4, 1, 2};
} else {
p = get(n - 2, k - 2);
p.push_back(n);
p.push_back(n - 1);
}
return p;
}
void solve() {
int n;
i64 k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
if (k < n) {
std::cout << -1 << "\n";
return;
}
auto p = get(n, k);
for (int i = 0; i < n; ++ i) {
std::cout << p[a[i] - 1] << " \n"[i == n - 1];
}
}
J. 硝基甲苯之袭
题意:给你一个数组,有多少对
因为值域很小,那么我们枚举每个数的约数
点击查看代码
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";
}
K. 硝基甲苯之魇
题意:给你一个数组,求有多少区间满足区间
因为
并且连续做
因为最多跳
点击查看代码
#define ls (u << 1)
#define rs (u << 1 | 1)
#define umid (tr[u].l + tr[u].r >> 1)
struct Node {
int l, r;
int d;
};
struct SegmentTree {
std::vector<Node> tr;
SegmentTree(int _n) {
tr.assign(_n << 2, {});
build(1, _n);
}
void pushup(int u) {
tr[u].d = std::gcd(tr[ls].d, tr[rs].d);
}
void build(int l, int r, int u = 1) {
tr[u] = {l, r};
if (l == r) {
return;
}
int mid = l + r >> 1;
build(l, mid, ls); build(mid + 1, r, rs);
}
void modify(int p, int x) {
int u = 1;
while (tr[u].l != tr[u].r) {
int mid = umid;
if (p <= mid) {
u = ls;
} else {
u = rs;
}
}
tr[u].d = x;
u >>= 1;
while (u) {
pushup(u);
u >>= 1;
}
}
int query(int l, int r, int u = 1) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].d;
}
int mid = umid;
if (r <= mid) {
return query(l, r, ls);
} else if (l > mid) {
return query(l, r, rs);
}
return std::gcd(query(l, r, ls), query(l, r, rs));
}
};
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
std::vector<int> sum(n + 1);
SegmentTree tr(n);
std::map<int, std::vector<int> > pos;
pos[0].push_back(0);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
sum[i + 1] = sum[i] ^ a[i];
pos[sum[i + 1]].push_back(i + 1);
tr.modify(i + 1, a[i]);
}
i64 ans = 0;
for (int i = 1; i <= n; ++ i) {
int j = i - 1;
while (j) {
int d = tr.query(j, i);
int l = 1, r = j;
while (l < r) {
int mid = l + r >> 1;
if (tr.query(mid, i) == d) {
r = mid;
} else {
l = mid + 1;
}
}
int t = sum[i] ^ d;
int L = std::lower_bound(pos[t].begin(), pos[t].end(), l - 1) - pos[t].begin();
int R = std::upper_bound(pos[t].begin(), pos[t].end(), j - 1) - pos[t].begin() - 1;
if (L <= R) {
ans += R - L + 1;
}
j = l - 1;
}
}
std::cout << ans << "\n";
}
L. 一念神魔之耀
题意:
我们可以操作任何长度为
点击查看代码
void solve() {
int n, x, y;
std::cin >> n >> x >> y;
std::string s;
std::cin >> s;
if (x < y) {
std::swap(x, y);
}
std::vector<std::array<int, 2> > ans;
auto op = [&](int l, int r) {
ans.push_back({l, r});
for (int i = l; i <= r; ++ i) {
s[i] ^= 1;
}
};
int d = std::gcd(x, y);
for (int i = 0; i + y - 1 < n; ++ i) {
if (s[i] == '0') {
op(i, i + y - 1);
}
}
for (int i = n - 1; i + y - 1 >= n; -- i) {
if (s[i] == '0') {
op(i - x + 1, i);
int l = i - x + 1;
while (1) {
int j = l;
while (j + y - 1 <= i - d) {
op(j, j + y - 1);
j += y;
}
if (j == i - d + 1) {
break;
}
op(j - x, j - 1);
l = j - x;
}
}
}
for (auto & c : s) {
if (c == '0') {
std::cout << -1 << "\n";
return;
}
}
std::cout << ans.size() << "\n";
for (auto & [l, r] : ans) {
std::cout << l + 1 << " " << r + 1 << "\n";
}
}
M. 数值膨胀之美
题意:给你一个数组,你要恰好执行一次操作,选择一段区间让这个区间的数都乘2。最小化极差。
要让影响极差,那么我们肯定要改最大值和最小值,发现改最大值只会变大。那么我们应该操作最小值。随便找一个最小值的位置,然后两边扩展,看是不是最小值然后乘
点击查看代码
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";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!