一小片夕阳落在我手上.|

Natural_TLP

园龄:5个月粉丝:3关注:2

2025-02-26 12:18阅读: 5评论: 0推荐: 0

2025牛客寒假算法基础集训营3补题笔记

比赛链接

题目难度顺序大致为:\(A、M、F、L、C、\)

\(easy\)\(A、M、F、L、C\)

太难了这场。。。E题卡了3个多小时。。。

A.智乃的博弈游戏

题意

\(n\) 个石头,两人轮流取石头。每次能取小于石头个数且与石头个数互质的数量,当某人取时只有一颗石头则获胜。问先手是否可以必胜。

思路

博弈论,思维

首先,相邻的奇数是互质的,即当 \(n\) 是奇数时,\(n - 2\) 一定和其互质。所以,奇数的局面一定可以留下2个石头给下一个人,而2个石头是必输的局面。
接着考虑当 \(n\) 是偶数时,偶数只能和奇数互质,而偶数-奇数=奇数,留奇数个石头给下一个人,那么下一个人是必胜的,理由如上。
综上所述,先手是奇数个石头必胜,偶数个石头必败。

注意开long long!

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

代码

点击查看代码
#include <iostream>
#define ll long long
using namespace std;
ll x;
void solve()
{
cin >> x;
if (x % 2) cout << "Yes";
else cout << "No";
}
int main()
{
int t = 1;
while (t --) solve();
return 0;
}

M.智乃的牛题

题意

给一个字符串,询问该字符串的字母能否组成单词 \(nowcoder\)

思路

模拟
记录每个字母次数,然后查看是否可以拼成 \(nowcoder\) 即可。

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

代码

点击查看代码
#include <iostream>
#include <map>
using namespace std;
string s;
map<char, int> mp;
string tmp = "nowcoder";
void solve()
{
cin >> s;
for (auto i : s) mp[i] ++;
for (auto i : tmp)
if (i == 'o' && mp[i] == 2) continue;
else if (mp[i] == 1) continue;
else return void(cout << "I AK IOI");
cout << "happy new year";
}
int main()
{
int t = 1;
while (t --) solve();
return 0;
}

F.智乃的捉迷藏

题意

三个人分别可以看见躲藏在面前三个位置上的人,一共有n个人躲藏,询问三人是否可以分别看见\(a、b、c\)个人。
即:\(a = s_1 + s_2 + s_3, b = s_3 + s_4 + s_5, c = s_5 + s_6 + s_1\)

思路

数学
解上面的等式:

\[a + b + c = 2 \times (s_1 + s_3 + s_5) + (s_2 + s_4 + s_6) \\ n = (s_1 + s_3 + s_5) + (s_2 + s_4 + s_6) \\ \]

\[(s_1 + s_3 + s_5) = a + b + c - n \ge 0 \\ (s_2 + s_4 + s_6) = 2n - (a + b + c) \ge 0 \]

\[n \le a + b + c \le 2n \]

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

代码

点击查看代码
#include <iostream>
using namespace std;
int n;
int a, b, c;
void solve()
{
cin >> n >> a >> b >> c;
if (a + b + c <= 2 * n && a + b + c >= n) cout << "Yes" << '\n';
else cout << "No" << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t --) solve();
return 0;
}

L.智乃的三角遍历

题意

给一个 \(n\) 层的三角形,询问是否可以一笔不重不漏的经过三角形上的所有边,如果可以就给出一组序列表示经过的顶点顺序。

思路

模拟

顺序不止一种,自己纸上找到一种规律然后想办法模拟下来就好了。

我的顺序是每层画完后接着画下一层

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

代码

点击查看代码
#include <iostream>
#include <vector>
using namespace std;
const int M = 1000 + 10;
int n, m;
vector<int> g[M];
vector<int> ans;
void solve()
{
cin >> n;
int num = 1;
for (int i = 0; i <= n; i ++) {
int j = i + 1;
while (j --) g[i].emplace_back(num ++);
}
cout << "Yes" << '\n';
for (int i = 0; i <= n; i ++) {
for (int j = 0; j < g[i].size(); j ++) cout << g[i][j] << ' ';
if (i + 1 > n) continue;
for (int j = g[i + 1].size() - 2; j >= 1; j --)
cout << g[i + 1][j] << ' ' << g[i][j - 1] << ' ';
}
for (int i = n - 1; i >= 0; i --)
cout << g[i].back() << ' ';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
while (t --) solve();
return 0;
}

C.智乃的Notepad(Easy version)

题意

\(n\) 个单词,询问最少敲击键盘次数,在使用键盘输入过程中出现所有单词,删除键敲击也算一次。

思路

思维、贪心、字典树、\(dfs\)

存储多个单词,可以想到使用字典树来将所有单词存下。

那么问题就会转换为,遍历这颗字典树上所有节点的最小步数。

可以想到,如果是从根节点出发后再回到根节点,那么所有节点都会遍历两次。而我们并不需要回到根节点,要么从贪心角度出发,我们只需要停留在最深那个节点上即可,这样我们就不要将这最深的路径上的节点又遍历一次,答案为:所有节点个数乘二减去最深的层数。

时间复杂度:\(O(\sum|s_i| \times 26)\)

代码

点击查看代码
#include <iostream>
#define ll long long
#define si(x) int(x.size())
using namespace std;
const int N = 1e6 + 10;
const int M = 30;
int n, m;
int son[N][M], idx;
ll maxv;
void insert(string s)
{
int p = 0;
for (int i = 0; i < si(s); i ++) {
int u = s[i] - 'a';
if (!son[p][u]) son[p][u] = ++ idx;
p = son[p][u];
}
}
ll dfs(int u, ll stp)
{
ll res = 1;
maxv = max(maxv, stp);
if (u == 0) res = 0;
for (char i = 'a'; i <= 'z'; i ++) {
int v = i - 'a';
if (!son[u][v]) continue;
ll sum = dfs(son[u][v], stp + 1);
res += sum;
}
return res;
}
void solve()
{
cin >> n >> m;
for (int i = 0; i < n; i ++) {
string s;
cin >> s;
insert(s);
}
int l, r;
cin >> l >> r;
int ans = dfs(0, 0);
cout << ans * 2ll - maxv;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
while (t --) solve();
return 0;
}

E.智乃的小球

题意

\(n\) 个小球,以及每个小球的位置 \(p_i\) 和方向 \(v_i\) ,求第 \(k\) 对完全弹性碰撞在什么时刻发生。

思路

二分、双指针

首先,假设在时刻 \(x\) 时发生了 \(k\) 对完全弹性碰撞,那么在大于 \(x\) 的时刻肯定发生了不小于 \(k\) 对的完全弹性碰撞,在小于 \(x\) 的时刻肯定没有达到 \(k\) 对完全弹性碰撞,所以时刻是具备二分性的,考虑用二分来解决。

接着,就是判断二分的时刻下发生了多少对完全弹性碰撞。
在此之前,还要知道一个点,就是两个小球完全弹性碰撞,可以看成两个小球交错而过。
假设现在向右的小球的位置为:2 4 6 8,向左的小球的位置为:3 5 7,模拟小球运动:当 2 与 3 5两个小球交错而过(即发生了两次完全弹性碰撞,后面的也如此)那么肯定的,4 在此之前一定已经和 5 相遇了一次,现在我们二分了一个时刻,可以预知向右的小球在 \(x\) 时刻后会在 \(p_i + x\) 的位置上,那么在 \([p_i, p_i + x]\) 区间内的向左的小球都会相遇一次,由此我们可以统计出所有向右的小球的碰撞总次数,再和 \(k\) 进行比较。

再计算 \([p_i, p_i + x]\) 区间内的相遇次数时,不能暴力枚举所有向右的小球,可以使用双指针去检测区间内的向左的小球的左右下标。

代码

点击查看代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <iomanip>
using namespace std;
typedef long long ll;
int n, k;
vector<int> u, v;
bool check(ll x)
{
ll res = 0;
int l = 0, r = 0;
for (auto i : u) {
while (l < v.size() && v[l] < i) l ++;
while (r < v.size() && v[r] <= i + x) r ++;
res += r - l;
}
return res >= k;
}
int main()
{
cin >> n >> k;
for (int i = 0; i < n; i ++) {
int p, y;
cin >> p >> y;
if (y == 1) u.emplace_back(p);
else v.emplace_back(p);
}
sort(u.begin(), u.end());
sort(v.begin(), v.end());
ll l = 1, r = 1e9;
while (l < r) {
ll mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (!check(l)) cout << "No";
else cout << "Yes" << '\n' << fixed << setprecision(6) << 1.0 * l / 2;
return 0;
}

J.智乃画二叉树

题意

根据题目数据画二叉树。

思路

超级大模拟,不会。。。

代码


K.智乃的逆序数

题意

构造一个序列包含若干个值域互不相交子序列,且逆序对恰好为 \(K\) 对。

思路

求逆序数的方法有:冒泡排序、归并排序、树状数组、线段树等。

先简单化考虑,因为给出的是几段大小连续且重排的序列,那么先得出逆序数最少和逆序数最多的情况。
逆序数最少的情况就是每段从小到大进行段间排序,逆序数为每段内的逆序数之和。
最大的情况就是每段从大到小进行段间排序,逆序数要根据冒泡排序算出。

我们可以从最少的逆序数的情况开始构造,如果逆序数量不够,就在段间把各自两个元素进行位置交换,知道逆序数达到 \(k\) 对。

代码

点击查看代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> PII;
int n, k;
vector<PII> a;
int main()
{
cin >> n >> k;
vector<vector<int>> b(n);
for (int i = 0; i < n; i ++) {
int l;
cin >> l;
for (int j = 0; j < l; j ++) {
int x;
cin >> x;
b[i].emplace_back(x);
}
}
sort(b.begin(), b.end(), [](vector<int> a, vector<int> b){
return a[0] < b[0];
});
for (int i = 0; i < n; i ++)
for (int j : b[i])
a.emplace_back(i, j);
int sum = 0;
for (int i = 0; i < a.size(); i ++)
for (int j = i + 1; j < a.size(); j ++)
if (a[i] > a[j]) sum ++;
if (k < sum) return cout << "No", 0;
k -= sum;
auto print = [&]() -> void {
cout << "Yes" << '\n';
for (auto it : a)
cout << it.second << ' ';
};
if (k == 0) print();
else {
for (int i = 0; i < a.size() - 1; i ++)
for (int j = 0; j < a.size() - 1 - i; j ++)
if (a[j].first == a[j + 1].first) continue;
else if (k && a[j].second < a[j + 1].second) {
swap(a[j], a[j + 1]);
k --;
}
if (k) cout << "No";
else print();
}
return 0;
}

本文作者:Natural_TLP

本文链接:https://www.cnblogs.com/Natural-TLP/p/18721657

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

posted @   Natural_TLP  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.

作曲 : Reol

作词 : Reol

fade away...do over again...

fade away...do over again...

歌い始めの一文字目 いつも迷ってる

歌い始めの一文字目 いつも迷ってる

どうせとりとめのないことだけど

伝わらなきゃもっと意味がない

どうしたってこんなに複雑なのに

どうしたってこんなに複雑なのに

噛み砕いてやらなきゃ伝わらない

ほら結局歌詞なんかどうだっていい

僕の音楽なんかこの世になくたっていいんだよ

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.

目の前 広がる現実世界がまた歪んだ

目の前 広がる現実世界がまた歪んだ

何度リセットしても

僕は僕以外の誰かには生まれ変われない

「そんなの知ってるよ」

気になるあの子の噂話も

シニカル標的は次の速報

麻痺しちゃってるこっからエスケープ

麻痺しちゃってるこっからエスケープ

遠く遠くまで行けるよ

安定なんてない 不安定な世界

安定なんてない 不安定な世界

安定なんてない きっと明日には忘れるよ

fade away...do over again...

fade away...do over again...

そうだ世界はどこかがいつも嘘くさい

そうだ世界はどこかがいつも嘘くさい

綺麗事だけじゃ大事な人たちすら守れない

くだらない 僕らみんなどこか狂ってるみたい

本当のことなんか全部神様も知らない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.