牛客小白月赛65个人题解A-E

1. 牛客小白月赛65

A. 牛牛去购物

题意:给定n元,购买价格为a元的篮球和价格为b的篮球,数量不定,要使得花掉的钱最多,也就是剩余的钱数最少,求这个值 (1 <= n, a, b <= 1000)
思路:因为数据范围很小, 故直接双重循环暴力枚举购买篮球和足球的数量即可,注意一些取值范围,然后取min可得剩余的钱数最小值
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'

void solve()
{
    int n, a, b;
    cin >> n >> a >> b;
    
    int res = 1e8;
    
    for (int i = 0; i <= n / a; i ++)
    {
        for (int j = 0; i * a + j * b <= n; j ++)
        {
            res = min(res, n - i * a - j * b);
        }
    }
    cout << res << endl;
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
//     cin >> T;
    T = 1;
    while (T --) solve();
    return 0;
}

B.牛牛写情书

题意:给定s1字符串(仅包含小写字母a-z),而后对其某些位置插入s2 = “0123456789±*|,.~!@#$%^&()[]{}'”;:?<>/" 的子串,然后给定s3字符串,询问s1原串中是否有子串等于s3, (字符串长度 <= 5e3)
思路:由于给出的s1是已经被s2插入过的字符串,可以对照着s2字符串,还原出真正的s1字符串,然后普通的字符串查找子串即可, 值得注意的是:对于’\’ 和 ’ " ’ 这两个字符需要在其前面 加一个 ‘\’ 进行转义
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'


void solve()
{
    int n, m;
    cin >> n >> m;
    
    string s1, s2;
    cin >> s1 >> s2;
    
    string s3 = "0123456789+-*|,.~!@#$%^&()[]{}'\";:?<>\\/";
    map<char, int> mp;
    for (int i = 0; i < s3.size(); i ++) mp[s3[i]] ++;
    
    string s4;
    for (int i = 0; i < s1.size(); i ++)
    {
        if (mp.count(s1[i])) continue;
        s4 += s1[i];
    }
    
    int n2 = s2.size();
//     cout << s4 << endl;
    for (int i = 0; i < s4.size(); i ++)
    {
        string t = s4.substr(i, n2);
        if (t == s2)
        {
            cout << "YES" << endl;
            return;
        }
    }

    cout << "NO" << endl;
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
    //cin >> T;
    T = 1;
    while (T --) solve();
    return 0;
}

C.牛牛排队伍

题意:n个人排队, 1号->2号->3号…, 以此类推n-1号排在n号的前面, 有两个操作,操作1:将x号叫走(保证x号此时一定在队伍中); 操作2:求排在x号前面的是谁
思路:对于这种查询前面是某个人的,很容易想到链表,而且是必须是双向链表,因为我们要做的操作是:将x移走,那么就要求x的前一个人是谁,x的后一个人是谁,所以需要pre数组记录x的前一个人是pre[x],同理suff[x]记录x的后一个是suff[x],然后更新的过程画个图直觉体验更好, ps:(这道题卡vector,vector只能过95%的数据) 😡
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'
const int N = 1e6 + 10;
int pre[N], suff[N];

void solve()
{
    int n, k;
    cin >> n >> k;
    
    for (int i = 1; i <= n; i ++) pre[i] = i - 1;
    for (int i = 0; i <= n; i ++) suff[i] = i + 1;
    
    while (k --)
    {
        int id, x;
        cin >> id >> x;
        
        if (id == 1)
        {
            int t_pre = pre[x];
            int t_suff = suff[x];
            
            pre[t_suff] = pre[x];
            suff[t_pre] = suff[x];
        }
        else
        {
            cout << pre[x] << endl;
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
//     cin >> T;
    T = 1;
    while (T --) solve();
    return 0;
}

D.牛牛取石子

题意:有两堆石子,第一堆有a个,第二堆有b个,牛牛先手,牛妹后手,对于取石子有两种方案:(1) 第一堆取1个,第二堆取2个; (2) 第一堆取2个,第二堆取1个;对于选择的方案,必须保证当前石子 >= 取的石子个数才能取,谁先无法取石子,谁就输了,输入两堆石子的个数,判断谁获胜
思路:这个涉及到对称博弈
  1. 对于后手来说,只要选择与先手对称相反的操作,即先手选择方案1,那么后手就选方案2,这样就可以保证对于两个石堆来说都是 -3,且先手没有办法反制,因此对于两堆石子中的最小值,只要它是 % 3 == 0,那么一定是后手必胜,因为最小值能更快达到0这个必败态
  2. 那么相反,对于如果 两堆石子中的最小值,它不是 % 3 == 0,比如说是 % 3 == 1, 那么先手就取那个有最小值的石堆石子1个,最大值石堆石子2个,这样就可以保证 有最小石子的石堆 % 3 == 0, 这样又变成了情况1了,因此此时是先手必胜
  3. 考虑特殊情况,如果这两个石堆的石子数量一样,那么有最小值的余数有3钟情况:0, 1, 2; 分类讨论可得,对于余数为0的情况,后生必胜(1); 对于余数为1的情况,后手必胜(举个例子:(4,4)), 对于余数为2的情况(5, 5), 先手必胜
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'

void solve()
{
    int a, b;
    cin >> a >> b;
    
    int r = min(a, b) % 3;
    
    if (r)
    {
        if (r == 1 and a == b) cout << "niumei" << endl;
        else cout << "niuniu" << endl;
    }
    else cout << "niumei" << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
    cin >> T;
//     T = 1;
    while (T --) solve();
    return 0;
}

E. 牛牛的构造

题意:给定数组a的长度n,数组a中的数是1~n的排列,求构造一个数组a,使得这个数组恰好存在k个二元组,满足 1 <= i < j <= n && a[i]-a[j] = 2^x (x属于非负整数) 如果不满足一个数组条件输出-1,反之输出这个数组
思路:考虑到要构成这种二元组,那么一定是逆序的,那么即有构造一个前部分顺序,后部分逆序的数组,这样就可以提供这种二元组,同时要考虑每个数字对于二元组个数的贡献
  1. 发现对于每个数字i, 满足 i + 2^x <= n,则有 x = log2(n - i), 再 + 1即为每个数的最大贡献,也就是 x + 1,例如对于数字1,它的贡献是 log2(5-1) + 1 = 3,因为向下取整的原因 + 1,同理对于数字2,log2(5-2) + 1 = 2
  2. 得到每个数字i的贡献后,将k总值减去对应的数字的贡献值,并将其放到最后
  3. 例如 5 5, 可得 3 4 5 2 1, 因为1贡献了3个二元组,2贡献了2个二元组
#include <bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'

const int N = 1e6 + 10;
int flag1[N], flag2[N];
int lg[N];

void solve()
{
    int n, k;
    cin >> n >> k;
    
    for (int i = 2; i <= n; i ++) lg[i] = lg[i >> 1] + 1;
    
    for (int i = 1; i <= n; i ++) flag1[i] = 1;
    
    for (int i = 1; i < n; i ++)
    {
        int cha = n - i;
        if (lg[cha] + 1 <= k)
        {
            flag1[i] = 0;
            flag2[i] = 1;
            k -= lg[cha] + 1;
        }
        
        if (k == 0)
            break;
    }
    
    if (k > 0)
    {
        cout << -1 << endl;
        return;
    }
    
    for (int i = 1; i <= n; i ++)
        if (flag1[i]) cout << i << " ";
    
    for (int i = n; i >= 1; i --)
        if (flag2[i]) cout << i << " ";
}


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    int T;
//     cin >> T;
    T = 1;
    while (T --) solve();
    return 0;
}
### 关于小白109的信息 目前并未找到关于小白109的具体比信息或题解内容[^5]。然而,可以推测该事可能属于网举办的系列算法之一,通常这类比会涉及数据结构、动态规划、图论等经典算法问题。 如果要准备类似的事,可以通过分析其他场次的比题目来提升自己的能力。例如,在小白13中,有一道与二叉树相关的题目,其核心在于处理树的操作以及统计最终的结果[^3]。通过研究此类问题的解决方法,能够帮助理解如何高效地设计算法并优化时间复杂度。 以下是基于已有经验的一个通用解决方案框架用于应对类似场景下的批量更新操作: ```python class TreeNode: def __init__(self, id): self.id = id self.weight = 0 self.children = [] def build_tree(n): nodes = [TreeNode(i) for i in range(1, n + 1)] for node in nodes: if 2 * node.id <= n: node.children.append(nodes[2 * node.id - 1]) if 2 * node.id + 1 <= n: node.children.append(nodes[2 * node.id]) return nodes[0] def apply_operations(root, operations, m): from collections import defaultdict counts = defaultdict(int) def update_subtree(node, delta): stack = [node] while stack: current = stack.pop() current.weight += delta counts[current.weight] += 1 for child in current.children: stack.append(child) def exclude_subtree(node, total_nodes, delta): nonlocal root stack = [(root, False)] # (current_node, visited) subtree_size = set() while stack: current, visited = stack.pop() if not visited and current != node: stack.append((current, True)) for child in current.children: stack.append((child, False)) elif visited or current == node: if current != node: subtree_size.add(current.id) all_ids = {i for i in range(1, total_nodes + 1)} outside_ids = all_ids.difference(subtree_size.union({node.id})) for idx in outside_ids: nodes[idx].weight += delta counts[nodes[idx].weight] += 1 global nodes nodes = {} queue = [root] while queue: curr = queue.pop(0) nodes[curr.id] = curr for c in curr.children: queue.append(c) for operation in operations: op_type, x = operation.split(' ') x = int(x) target_node = nodes.get(x, None) if not target_node: continue if op_type == '1': update_subtree(target_node, 1) elif op_type == '2' and target_node is not None: exclude_subtree(target_node, n, 1) elif op_type == '3': path_to_root = [] temp = target_node while temp: path_to_root.append(temp) if temp.id % 2 == 0: parent_id = temp.id // 2 else: parent_id = (temp.id - 1) // 2 if parent_id >= 1: temp = nodes[parent_id] else: break for p in path_to_root: p.weight += 1 counts[p.weight] += 1 elif op_type == '4': pass # Implement similarly to other cases. result = [counts[i] for i in range(m + 1)] return result ``` 上述代码片段展示了针对特定类型的树形结构及其操作的一种实现方式。尽管它并非直接对应小白109中的具体题目,但它提供了一个可借鉴的设计思路。 ####
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值