百度之星2022题目记录

1 介绍

本博客记录百度之星2022编程比赛相关题目。

2 训练–钻石level

题目1:小度养小猫

vip题目,未开放!

题目2BD202203简单题

解题思路:贪心。
贪心策略:上升序列升得越慢越好,下降序列降得越慢越好
先去除相邻相等元素的干扰。然后判断,如果当前元素b[i]既可以加入到上升序列中也可以加入到下降序列中,比较b[i]b[i+1]的大小,如果b[i]<b[i+1]那么将b[i]放入上升序列中,上升序列会升得慢一些。

C++代码如下,

#include<bits/stdc++.h> 

using namespace std;

const int N = 1e5 + 10;

int n;
int a[N];

int main( )
{
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }

    //相邻从重复元素不影响最终答案,把它去掉
    vector<int> b;
    for (int i = 1; i <= n; ++i) {
        if (b.empty() || b.back() != a[i]) {
            b.emplace_back(a[i]);
        }
    }

    int x = 0; //上升序列的末尾值
    int y = 1e9 + 10; //下降序列的末尾值
    for (int i = 0; i < b.size(); ++i) {
        if (x <= b[i] && y >= b[i]) { //b[i]可以加入上升序列中,也可以加入下降序列中
            //贪心:上升序列升得越慢越好,下降序列将得越慢越好
            if (i+1 < b.size() && b[i] < b[i+1]) {//将它加入上升序列中
                x = b[i];
            } else {
                y = b[i];
            }
        } else if (x <= b[i]) {
            x = b[i];
        } else if (y >= b[i]) {
            y = b[i];
        } else {
            cout << "no" << endl;
            return 0;
        }
    }

    cout << "yes" << endl;
    return 0;
}

题目3:大水题

vip题目,未开放!

题目4:和

vip题目,未开放。

题目5:课程安排

vip题目,未开放。

题目6BD202211逃离这棵树

解题思路:

f u f_u fu表示从 u u u出发到达叶结点的时间的数学期望,它可以由它的子结点 v v v f v f_v fv和它本身 f u f_u fu表示。具体而言,它的计算包含两部分,一是不停留部分,二是停留部分。

不停留部分:
∑ v ∈ { f a t h e r = u } ( f v + 1 ) ⋅ q u v p u + ∑ v ∈ { f a t h e r = u } q u v (1) \frac{\sum_{v\in \{father=u\}} (f_v + 1) \cdot q_{uv}} {p_u + \sum_{v \in \{father=u\}} q_{uv}} \tag{1} pu+v{father=u}quvv{father=u}(fv+1)quv(1)

停留部分:
p u p u + ∑ v ∈ { f a t h e r = u } q u v ( f u + 1 ) (2) \frac{p_u}{p_u + \sum_{v \in \{father=u\}} q_{uv}}(f_u+1) \tag{2} pu+v{father=u}quvpu(fu+1)(2)
公式(1)和公式(2)中的记号解释如下,

p u p_u pu表示结点 u u u的权值。

{ f a t h e r = u } \{father=u\} {father=u}表示父结点是 u u u的结点的集合,也即结点 u u u的子结点的集合。

v ∈ { f a t h e r = u } ,   q u v v\in \{father = u\},\ q_{uv} v{father=u}, quv表示从结点 u u u到结点 v v v的边的权值。

( f v + 1 ) (f_v+1) (fv+1) ( f u + 1 ) (f_u+1) (fu+1)中的 + 1 +1 +1表示时间过去了1秒。

那么, f u f_u fu可以表示为,
f u = ∑ v ∈ { f a t h e r = u } ( f v + 1 ) ⋅ q u v p u + ∑ v ∈ { f a t h e r = u } q u v + p u p u + ∑ v ∈ { f a t h e r = u } q u v ( f u + 1 ) (3) f_u = \frac{\sum_{v\in \{father=u\}} (f_v + 1) \cdot q_{uv}} {p_u + \sum_{v \in \{father=u\}} q_{uv}} + \frac{p_u}{p_u + \sum_{v \in \{father=u\}} q_{uv}}(f_u+1) \tag{3} fu=pu+v{father=u}quvv{father=u}(fv+1)quv+pu+v{father=u}quvpu(fu+1)(3)

将上述等式两边的 f u f_u fu进行合并,然后化简可得,
f u = ∑ v ∈ { f a t h e r = u } ( f v + 1 ) q u v + p u ∑ v ∈ { f a t h e r = u } q u v (4) f_u = \frac{\sum_{v \in \{father=u\}} (f_v + 1)q_{uv} + p_u}{\sum_{v\in \{father=u\}} q_{uv}} \tag{4} fu=v{father=u}quvv{father=u}(fv+1)quv+pu(4)

又因为结果需要对 m o d = 998244353 mod = 998244353 mod=998244353取模,而 f u f_u fu是一个分数形式,因此需要利用到逆元定理,将它转成乘法形式。

逆元定理:a / b % mod可以转换成a * x % mod形式,其中x称为bmod的乘法逆元。b存在乘法逆元的充分必要条件是bmod互质。特别的,如果mod是质数,那么 x = b m o d − 2 x=b^{mod-2} x=bmod2就是bmod的乘法逆元。

因此,将公式(4)中分子记作a、分母记作b,那么有,
f u = a b   %   m o d = a ⋅ b m o d − 2   %   m o d (5) f_u = \frac{a}{b}\ \%\ mod = a \cdot b^{mod-2}\ \% \ mod \tag{5} fu=ba % mod=abmod2 % mod(5)
其中 b m o d − 2 b^{mod-2} bmod2可以使用快速幂来计算。

C++代码如下,

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII; //first表示b,second表示w

const int N = 1e6 + 10;
const int mod = 998244353;
int n;
int p[N];
long long f[N];
unordered_map<int, vector<PII>> map_a_bs;

int qmi(int a, int k, int p) {
    long long res = 1;
    while (k) {
        if (k & 1) res = res * a % p;
        k >>= 1;
        a = (long long)a * a % p;
    }
    return res;
}

void dfs(int a) {
    long long x = p[a]; //分子
    long long y = 0; //分母

    for (auto [b, w] : map_a_bs[a]) {
        dfs(b);
        x = (x + (f[b] + 1) * w % mod) % mod;
        y = (y + w) % mod;
    }
    
    f[a] = x * qmi(y, mod - 2, mod) % mod;
    return;
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> p[i];
    map_a_bs.clear();
    for (int i = 2; i <= n; ++i) {
        int b = i;
        int a, w;
        cin >> a >> w;
        map_a_bs[a].emplace_back(make_pair(b,w));
    }

    dfs(1);

    cout << f[1] << endl;

    return 0;
}

题目7BD202213星球联通

解题思路:关键是理解c[i]的含义。题目中说了,可以免费连接任意k个星球,那么

  1. c[1]表示连接任意k+1个星球的花费。
  2. c[2]表示连接任意k+2个星球的花费。
  3. ……
  4. c[n-k]表示连接任意n个星球的花费。

按照题意,总共需要建立n-k条边,才能够使得这n个星球连通,它包括了直接建立的边和安装传送门得到的边。也即,

  1. 当我们直接建立了0条边,那么需要通过传送门建立n-k条边。
  2. 当我们直接建立了1条边,那么需要通过传送门建立n-k-1条边。
  3. 当我们直接建立了2条边,那么需要通过传送门建立n-k-2条边。
  4. ……
  5. 当我们直接建立了n-k条边,那么需要通过传送门建立0条边。

使用kruskal算法求直接建边的最小代价即可。

C++代码如下,

#include<bits/stdc++.h> 

using namespace std;

const int N = 3010, M = N * N / 2;
int n, k;
int p[N];
int c[N];

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

struct Point{
    int x;
    int y;
    int z;
}points[N];

struct Edge {
    int a;
    int b;
    long long w;
}edges[M];

long long get_d(int i, int j) {
    Point a = points[i];
    Point b = points[j];
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z);
}

int main( )
{
    cin >> n >> k;
    for (int i = 1; i <= n-k; ++i) {
        cin >> c[i];
    }
    for (int i = 1; i <= n; ++i) {
        cin >> points[i].x >> points[i].y >> points[i].z;
    }

    //初始化p
    for (int i = 1; i <= n; ++i) p[i] = i;

    //初始化dist
    int cnt = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = i+1; j <= n; ++j) {
            long long w = get_d(i,j);
            edges[cnt++] = {i, j, w};
        }
    }

    sort(edges, edges + cnt, [&](Edge &a, Edge &b) {
        return a.w < b.w;
    });

    long long res = c[n-k];
    long long edgeSum = 0;
    int connect_edge_cnt = 0;
    for (auto edge : edges) {
        if (connect_edge_cnt == n-k) {
            break;
        }

        int a = edge.a;
        int b = edge.b;
        long long w = edge.w;
        
        int fa = find(a);
        int fb = find(b);
        if (fa != fb) {
            p[fa] = fb;
            edgeSum += w;
            connect_edge_cnt += 1;
            res = min(res, edgeSum + c[n-k-connect_edge_cnt]);
        }
    }

    cout << res << endl;

    return 0;
}

题目8BD202217不等式组

解题思路:dfs。鉴于题目数据量比较小,因此假设如果不等式组有解,那么解一定落在[-10,10)之间。

C++代码如下,

#include <bits/stdc++.h>

using namespace std;

bool dfs(int m, int n, vector<vector<int>> &A, vector<int> &X, int index) {
    if (index == n) { //n个未知量均已确认完毕
        for (int i = 0; i < m; ++i) {
            int sum = 0;
            for (int j = 0; j < n; ++j) sum += A[i][j] * X[j];
            if (sum > A[i][n]) {
                return false;
            }
        }
        return true;
    }

    for (int val = -10; val < 10; ++val) { //X[index]的取值 //此处认为解肯定落在[-10,10)内
        X[index] = val;
        if (dfs(m, n, A, X, index + 1)) {
            return true;
        }
    }
    return false;
}

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n, m;
        cin >> n >> m;
        vector<vector<int>> A(m, vector<int>(n+1, 0));
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n+1; ++j) {
                cin >> A[i][j];
            }
        }
        vector<int> X(n, 0);
        if (dfs(m, n, A, X, 0)) {
            cout << "YES" << endl;
        } else {
            cout << "NO" << endl;
        }
    }

    return 0;
}

题目9:三个因子

vip题目,暂未开放!

题目10:字符计数

vip题目,暂未开放!

题目11BD202225子序列

解题思路:贪心。a ^ b <= a + b

C++代码如下,

#include<bits/stdc++.h> 

using namespace std;

const int N = 1e5 + 10;
int n;
int a[N];

int main( )
{
    cin >> n;
    long long res = 0;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        res += a[i];
    }

    cout << res << endl;

    return 0;
}

题目12:最大值

vip题目,暂未开放!

3 参考

2005年-2023年百度之星题集
百度之星2022

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YMWM_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值