比赛链接

A

题解

知识点:前缀和,二分。

找到小于等于 \(x\) 的最后一个物品,往前取 \(k\) 个即可,用前缀和查询。

时间复杂度 \(O(n + q\log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int a[100007];
ll sum[100007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, q;
cin >> n >> q;
for (int i = 1;i <= n;i++) cin >> a[i];
sort(a + 1, a + n + 1);
for (int i = 1;i <= n;i++) sum[i] = sum[i - 1] + a[i];
while (q--) {
int k, x;
cin >> k >> x;
int pos = upper_bound(a + 1, a + n + 1, x) - a - 1;
cout << sum[pos] - sum[max(0, pos - k)] << '\n';
}
return 0;
}

B

题解

知识点:博弈论。

显然,每次取 \(1\) 是最优策略,但先拿的人串长肯定大于等于后拿的人,因此偶数平局,奇数后手胜。

时间复杂度 \(O(1)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
if (n & 1) cout << "Yaya-win!" << '\n';
else cout << "win-win!" << '\n';
return 0;
}

C

题解

知识点:数学。

分类讨论:

  1. \(a,b\) 串长相等时,只有 \(a = b\) 时结果是 = ,否则找到第一个不同的位置可以任意修改大小关系,结果是 !

  2. \(a,b\) 串长不同时,不妨假设 \(a\) 比 \(b\) 长,那么首先就一定有 \(a>b\) 的情况,考虑 \(a\) 最小的情况会不会造成 \(a\leq b\) 。

    若 \(a\) 超出 \(b\) 的部分不是相同的数,则一定不能全变成前导 \(0\) 此时无论如何一定有 \(a>b\) ,结果为 > ;否则,先 \(a\) 超出部分全部变成前导 \(0\) ,此时 \(a,b\) 长度相同。

    之后,若 \(a = b\) 则结果为 ! 。否则,找到第一对不同的数,如果 \(b\) 的数是 \(a\) 前导数,则 \(a\) 无论如何都有 \(a>b\) ,结果为 > ,否则结果为 !

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
string a, b;
cin >> a >> b;
if (a.size() == b.size()) {
if (a == b) cout << "=" << '\n';
else cout << "!" << '\n';
return 0;
}
char ans = '>';
if (a.size() < b.size()) {
swap(a, b);
ans = '<';
}
char zero = a[0];
for (int i = 0;i < a.size() - b.size();i++) {
if (a[i] != zero) {
cout << ans << '\n';
return 0;
}
}
a.erase(a.begin(), a.begin() + a.size() - b.size());
if (a == b) {
cout << "!" << '\n';
return 0;
}
for (int i = 0;i < a.size();i++) {
if (a[i] != b[i]) {
if (b[i] == zero) cout << ans << '\n';
else cout << "!" << '\n';
return 0;
}
}
return 0;
}

D

题解

知识点:优先队列,枚举。

维护一个过关顺序,按照区间左端点从小到大排序即可,每次看看左端点最小的是不是小于等于自己在位置加 \(1\) ,然后更新右端点。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; pair<int, int> a[100007], b[100007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i].first >> a[i].second;
for (int i = 1;i <= n;i++) cin >> b[i].first >> b[i].second;
priority_queue <pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq1, pq2;
int pos1 = 0, pos2 = 0;
for (int i = 1;i <= n;i++) {
pq1.push(a[i]);
pq2.push(b[i]);
while (pq1.size() && pq1.top().first <= pos1 + 1) {
pos1 = max(pos1, pq1.top().second);
pq1.pop();
}
while (pq2.size() && pq2.top().first <= pos2 + 1) {
pos2 = max(pos2, pq2.top().second);
pq2.pop();
}
if (pos1 > pos2) {
cout << "sa_win!" << '\n';
cout << pos1 - pos2 << '\n';
}
else if (pos1 < pos2) {
cout << "ya_win!" << '\n';
cout << pos2 - pos1 << '\n';
}
else {
cout << "win_win!" << '\n';
cout << 0 << '\n';
}
} return 0;
}

E

题解

知识点:构造。

考虑将排列 \([1,2,\cdots,n] \to [n,n-1,\cdots ,1]\) ,这样每个数必然和其他数相遇一次。

每次交换全部顺序对,一定保证在 \(n\) 轮内完成交换。

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
vector<int> v(n + 1);
iota(v.begin(), v.end(), 0);
vector<vector<pair<int, int>>> ans;
while (!is_sorted(v.begin() + 1, v.end(), greater<int>())) {
vector<pair<int, int>> t;
for (int i = 1;i < n;i++) {
if (v[i] < v[i + 1]) {
swap(v[i], v[i + 1]);
t.push_back({ v[i],v[i + 1] });
i++;
}
}
ans.push_back(t);
}
cout << ans.size() << '\n';
for (auto i : ans) {
cout << i.size() << '\n';
for (auto [x, y] : i) cout << x << ' ' << y << '\n';
}
return 0;
}

F

题解

方法一

知识点:单调栈,贪心。

因为根据字典序,所以我们要尽可能使前面的字母更大。

考虑用单调栈维护前缀字典序最大的子序列,同时需要维护 \(k\) ,若 \(k = 0\) 则不能再弹出元素。若到最后 \(k>0\) ,则还可以对得到的子序列从后往前弹出,可能使字典序更大。将弹出的元素计数,最后从大到小一并加到子序列后面。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

方法二

知识点:枚举,贪心。

预处理 \(nxt_{i,j}\) 表示字符串 \([i,n]\) 的位置中字母 \(j\) 第一个出现的位置。随后只需要从大到小枚举 \(j\) ,找到最大的且能操作到的字母 \(j\) ,将 \(s_i\) 到 \(j\) 的所有元素全部操作一遍,并计数。

处理出来的是字典序最大的子序列,此时若 \(k\) 还有剩下的,那就从后往前依次弹出子序列元素,同样计数。

最后将跳过的或弹出的元素从大到小安排到子序列后面即可。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

方法一

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, k;
cin >> n >> k;
string s;
cin >> s;
array<int, 26> cnt{};
string ans;
for (auto ch : s) {
while (k && ans.size() && ans.back() < ch) {
cnt[ans.back() - 'a']++;
ans.pop_back();
k--;
}
ans += ch;
}
while (k && ans.size()) {
cnt[ans.back() - 'a']++;
ans.pop_back();
k--;
}
for (int i = 25;i >= 0;i--) ans += string(cnt[i], i + 'a');
cout << ans << '\n';
return 0;
}

方法二

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int nxt[100007][26];
int cnt[26];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, k;
cin >> n >> k;
string s;
cin >> s;
for (int i = 0;i < 26;i++) nxt[n][i] = -1;
for (int i = n - 1;i >= 0;i--) {
for (int j = 0;j <= 25;j++) nxt[i][j] = nxt[i + 1][j];
nxt[i][s[i] - 'a'] = i;
}
string ans;
for (int i = 0, nnxt;i < s.size(); i = nnxt + 1) {
for (int j = 25;j >= 0;j--) {
if (~nxt[i][j] && nxt[i][j] - i <= k) {
nnxt = nxt[i][j];
break;
}
}
for (int j = i;j < nnxt;j++) cnt[s[j] - 'a']++;
ans += s[nnxt];
k -= nnxt - i;
}
while (k && ans.size()) {
cnt[ans.back() - 'a']++;
ans.pop_back();
k--;
}
for (int i = 25;i >= 0;i--) if (cnt[i]) ans += string(cnt[i], i + 'a');
cout << ans << '\n';
return 0;
}

G

题解

知识点:二分图,图匹配。

对于每个需要填的位置,由两侧已知的数字可以得到两个待选的数字,如 \(0,x,5 \to x = 1,4\) ,除了最后一个空位因为只需要和第 \(n-1\) 个数字有一位不同,所以有更多可能。

注意到每个数字只能选一次,换句话说,空位和数字是一一配对关系。考虑画成二分图,题目保证一定有解,所以只要匹配一次即可。可以使用匈牙利算法或者网络流求解二分图匹配,这里采用匈牙利算法。

直接使用复杂度是跑满 \(O(n^2)\) 的(但是交了也能过?),根本原因是每次跑完一个点的增广路,需要把访问标记全清空,复杂度是 \(O(n)\) 的。但实际上没有必要,考虑对于某次增广失败的所有右部点,他们的左部配对没有任何变化,无论你用哪个其他的左部点去配对这些增广失败的右部点,这些右部点都会走原来的路径,而原来的路径依旧是失败的,根本没必要再走一次。因此,我们每次配对的时候,如果某个右部点增广失败了,就保留访问标记,不需要清空,而只清空成功的增广路上的所有访问标记,这个只需要在dfs最后加上清空即可,能让算法跑的巨快。

特别地在这道题,每个点最多被访问四次就会被永久标记,复杂度变成线性。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; struct Graph {
struct edge {
int v, nxt;
};
int idx;
vector<int> h;
vector<edge> e; Graph(int n = 0, int m = 0) { init(n, m); } void init(int n, int m) {
idx = 0;
h.assign(n + 1, 0);
e.assign(m + 1, {});
} void add(int u, int v) {
e[++idx] = { v,h[u] };
h[u] = idx;
}
}; const int N = (1 << 16) + 7, M = N;
Graph g;
int n;
int a[N]; int p[M], vis[M];
bool dfs(int u) {
for (int i = g.h[u];i;i = g.e[i].nxt) {
int v = g.e[i].v - n - 1;
if (vis[v]) continue;
vis[v] = 1;
if (p[v] && !dfs(p[v])) continue;
p[v] = u;
vis[v] = 0;
return true;
}
return false;
}
int Hungary() {
int cnt = 0;
for (int i = 2;i <= n;i += 2) cnt += dfs(i);
return cnt;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
g.init(2 * n, 2 * n + n);
for (int i = 1;i <= n;i += 2) cin >> a[i], vis[a[i]] = 1;
for (int i = 2;i < n;i += 2) {
int t = a[i - 1] ^ a[i + 1];
int x1 = a[i - 1] ^ (t & -t);
t -= t & -t;
int x2 = a[i - 1] ^ (t & -t);
if (!vis[x1]) g.add(i, x1 + n + 1);
if (!vis[x2]) g.add(i, x2 + n + 1);
}
for (int i = 0;i < 16 && (1 << i) <= a[n - 1];i++) {
int x = a[n - 1] ^ (1 << i);
if (!vis[x]) g.add(n, x + n + 1);
}
Hungary();
for (int i = 0;i < n;i++) if (p[i]) a[p[i]] = i;
for (int i = 1;i <= n;i++) cout << a[i] << ' ';
cout << '\n';
return 0;
}

H

题解

知识点:模拟。

直接按照要求模拟即可。

时间复杂度 \(O(n)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int x, y, k, n;
ll t;
cin >> x >> y >> k >> n >> t;
ll sum = 0, cnt = 0, nowc = x;
for (int i = 1;i <= n;i++) {
sum += nowc * (n - i + 1);
cnt += n - i + 1;
nowc = x + cnt / k * y;
if (sum >= t) {
cout << i << '\n';
return 0;
}
}
cout << -1 << '\n';
return 0;
}

I

题解

知识点:贪心。

为了保证一定不会输,我们从后往前考虑。

若最后一局才一赢,但赢回本至少需要留 \(\left\lceil \dfrac{m}{2} \right\rceil\) ,但我们希望在前面赢的多,因此我们留最少的 \(\left\lceil \dfrac{m}{2} \right\rceil\) ,则剩下 \(\left\lfloor \dfrac{m}{2} \right\rfloor\) ;若在 \(n-1\) 局才赢,那么至少需要留除去第 \(n\) 局剩下部分的一半取上整,同样的我们留最少的就行,以此类推,最后剩下的所有给第一局,若中途不够分,则无解。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; ll a[100007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
ll m;
cin >> n >> m;
for (int i = n;i >= 1;i--) {
if (!m) {
cout << -1 << '\n';
return 0;
}
if (i == 1) a[i] = m;
else {
a[i] = (m + 1) / 2;
m -= (m + 1) / 2;
}
}
for (int i = 1;i <= n;i++) cout << a[i] << " \n"[i == n];
return 0;
}

J

题解

知识点:构造

属于灵光一现题qwq。

我们需要尽可能构造一条单向通道,这样才能保证尽可能多的格子都会被踩到。

可以考虑螺旋形,先考虑偶数情况,例如 \(n=6\) 从 \((1,1)\) 出发向右:

\[\begin{pmatrix}
0 & 0 & 0 & |1 & 1 & 1\\
2 & 2 & 2 & |0 & 0 & 1\\
2 & 1 & 1 & |\underline 2 & \underline0 & \underline1\\
2 & 1 & 0 & 0 & 1 & 2\\
2 & 1 & 1 & 1 & 1 & 2\\
2 & 2 & 2 & 2 & 2 & 2\\
\end{pmatrix}
\]

注意到每次经过下划线处会加 \(1\) 。

奇数情况取偶数情况左下角矩形即可,出发点在 \((n,n)\) 。

具体实现可以看代码。

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int a[1007][1007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
bool odd = n & 1;
if (odd) cout << n << ' ' << n << '\n', n++;
else cout << 1 << ' ' << 1 << '\n';
int x = 1, y = 1, cur = 0;
for (int i = 1;i <= n / 2;i++) {
for (int j = 1;j <= n - 2 * (i - 1) - 1;j++) {
a[x][y] = cur;
if (y == n / 2) (cur += 1) %= 3;
y++;
}
for (int j = 1;j <= n - 2 * (i - 1) - 1;j++) {
a[x][y] = cur;
if (x == n / 2) (cur += 1) %= 3;
x++;
}
for (int j = 1;j <= n - 2 * (i - 1) - 1;j++) {
a[x][y] = cur;
y--;
}
for (int j = 1;j <= n - 2 * (i - 1) - 1;j++) {
a[x][y] = cur;
x--;
}
x++, y++;
}
if (odd) n--;
for (int i = 1 + odd;i <= n + odd;i++)
for (int j = 1;j <= n;j++)
cout << a[i][j] << " \n"[j == n];
cout << n * n - 1 << '\n';
return 0;
}

K

题解

知识点:贪心。

每次最多可以淘汰 \(\left\lfloor \dfrac{x}{2} \right\rfloor\) 个人,只要取 \(\left\lceil \dfrac{x}{2} \right\rceil\) 即可。

特别地,若 \(x = 1,2\) ,则一定无法继续淘汰人。

时间复杂度 \(O(\log x)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll x;
cin >> x;
int cnt = 0;
while (x > 2) {
x = x / 2 + 1;
cnt++;
}
cout << cnt << '\n';
return 0;
}

L

题解

知识点:线性dp。

设 \(f_i\) 为剩余 \(i\) 个人的最小花费。初始状态为 \(f_n = 0\) 其他无穷大,转移方程见代码。

最后从小到大遍历一次就能获得人数最小的最小花费。

时间复杂度 \(O(nm)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int b[507], x[507];
ll f[100007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1;i <= m;i++) cin >> b[i] >> x[i];
for (int i = 1;i <= n;i++) f[i] = 1e18;
f[n] = 0;
for (int i = n;i >= 3;i--) {
if (f[i] >= 1e18) continue;
for (int j = 1;j <= m;j++) {
if (i >= x[j]) f[i / x[j] * x[j]] = min(f[i / x[j] * x[j]], f[i] + b[j]);
}
}
for (int i = 1;i <= n;i++) {
if (f[i] < 1e18) {
cout << f[i] << '\n';
break;
}
}
return 0;
}

2023牛客寒假算法基础集训营5 A-L的更多相关文章

  1. 2020牛客寒假算法基础集训营2 J题可以回顾回顾

    2020牛客寒假算法基础集训营2 A.做游戏 这是个签到题. #include <cstdio> #include <cstdlib> #include <cstring ...

  2. 2020牛客寒假算法基础集训营1 J题可以回顾回顾

    2020牛客寒假算法基础集训营1 这套题整体来说还是很简单的. A.honoka和格点三角形 这个题目不是很难,不过要考虑周全,面积是1,那么底边的长度可以是1也可以是2, 注意底边1和2会有重复的, ...

  3. Applese 的毒气炸弹 G 牛客寒假算法基础集训营4(图论+最小生成树)

    链接:https://ac.nowcoder.com/acm/contest/330/G来源:牛客网 Applese 的毒气炸弹 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 262 ...

  4. 牛客寒假算法基础集训营3B 处女座的比赛资格(用拓扑排序解决DAG中的最短路)

    链接:https://ac.nowcoder.com/acm/contest/329/B 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言5242 ...

  5. 牛客寒假算法基础集训营4 I题 Applese 的回文串

    链接:https://ac.nowcoder.com/acm/contest/330/I 来源:牛客网 自从 Applese 学会了字符串之后,精通各种字符串算法,比如--判断一个字符串是不是回文串. ...

  6. 牛客寒假算法基础集训营4 I Applese 的回文串

    链接:https://ac.nowcoder.com/acm/contest/330/I来源:牛客网 自从 Applese 学会了字符串之后,精通各种字符串算法,比如……判断一个字符串是不是回文串. ...

  7. 牛客寒假算法基础集训营2 【处女座与复读机】DP最小编辑距离【模板题】

    链接:https://ac.nowcoder.com/acm/contest/327/G来源:牛客网 一天,处女座在牛客算法群里发了一句“我好强啊”,引起无数的复读,可是处女座发现复读之后变成了“处女 ...

  8. 欧拉函数-gcd-快速幂(牛客寒假算法基础集训营1-D-小a与黄金街道)

    题目描述: 链接:https://ac.nowcoder.com/acm/contest/317/D来源:牛客网小a和小b来到了一条布满了黄金的街道上.它们想要带几块黄金回去,然而这里的城管担心他们拿 ...

  9. 牛客寒假算法基础集训营3处女座和小姐姐(三) (数位dp)

    链接:https://ac.nowcoder.com/acm/contest/329/G来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...

  10. 牛客寒假算法基础集训营4 F Applese 的大奖

    链接:https://ac.nowcoder.com/acm/contest/330/H来源:牛客网 Applese 和它的小伙伴参加了一个促销的抽奖活动,活动的规则如下:有一个随机数生成器,能等概率 ...

随机推荐

  1. 动态爱心-详细教程(小白也会)(HTML)

    动态爱心 超级超级超级简单!!!赶紧做给你们的"Ta"看吧! (最后有详细步骤) 视频效果: 话不多说直接上代码 点击查看代码 <!DOCTYPE HTML PUBLIC & ...

  2. promise 的串行执行

    function pri (num) {   return new Promise((resolve,reject) => {     console.log('开始'+num)     res ...

  3. 如何避免由 Web 字体引起的布局偏移

    前言 一些布局上的完全加载前后的变化很容易解决:为动态元素预先分配正确的空间,在图像上使用宽度和高度属性,并优先考虑 HTML 文档中的可见元素.但是,导致布局偏移的还有一个难以解决的问题:无样式文本 ...

  4. mindxdl--common--logger.go

    // Copyright (c) 2021. Huawei Technologies Co., Ltd. All rights reserved.// Package common the contr ...

  5. i春秋GetFlag

    进去是个提示界面,提示我们这是个迷你文件管理系统,我们需要登录然后下载文件再获得flag. 然后我们查看源码,没什么信息,点login进去查看源码,没什么信息 下方出现了一个substr(md5(ca ...

  6. EntityUtils MapStruct BeanCopier 数据实体类转换工具 DO BO VO DTO 附视频

    一.序言 在实际项目开发过程中,总有数据实体类互相转换的需求,DO.BO.VO.DTO等数据模型转换经常发生.今天推荐几个好用的实体类转换工具,分别是EntityUtils MapStruct Bea ...

  7. Cesium-03:洪水淹没

    Cesium-01:Vue 中基础使用 Cesium-02:飞机模型简单点对点飞行 Cesium-03:洪水淹没 前言 最开始想做洪水淹没的话,查了一些资料.又基于不同的实现的,如 ArcScene ...

  8. docker给已存在的容器添加或修改端口映射

    简述: 这几天研究了一下docker, 发现建立完一个容器后不能增加端口映射了,因为 docker run -p 有 -p 参数,但是 docker start 没有 -p 参数,让我很苦恼,无奈谷歌 ...

  9. Springboot 2.3.1配置拦截器遇到的坑

    1.多个配置类继承WebConfigureSupport或实现WebConfigure接口导致拦截器失效 2.拦截器中的bean无法正常注入,SpringBoot项目的Bean装配默认规则是根据App ...

  10. Flutter和Rust如何优雅的交互

    前言 文章的图片链接都是在github上,可能需要...你懂得:本文含有大量关键步骤配置图片,强烈建议在合适环境下阅读 Flutter直接调用C层还是蛮有魅力,想想你练习C++,然后直接能用flutt ...