百度之星2023题目记录

1 介绍

本博客记录百度之星比赛的题目及解法。

2 训练-钻石level

题目1BD202301公园

C++代码如下,

解题思路:计算起始结点a到所有结点的最小距离(因为是等长边,所以可以用bfs),计算结点b到所有结点的最小距离,计算终点n到所有结点的最小距离。依次枚举中间结点,更新最小花费。

#include<bits/stdc++.h> 

using namespace std;

const int N = 40010;

int wa, wb, wc;
int sa, sb;
int n, m;
int da[N];
int db[N];
int dc[N];
unordered_map<int, vector<int>> map_a_bs;

void bfs(int a, int d[]) {
    for (int i = 0; i < N; ++i) {
        d[i] = 0x3f3f3f3f;
    }

    queue<int> q;
    d[a] = 0;
    q.push(a);

    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        for (int b : map_a_bs[t]) {
            if (d[b] == 0x3f3f3f3f) {
                d[b] = d[t] + 1;
                q.push(b);
            }
        }
    }
    return;
}

int main( )
{
    map_a_bs.clear();
    cin >> wa >> wb >> wc;
    cin >> sa >> sb >> n >> m;
    for (int i = 0; i < m; ++i) {
        int a, b;
        cin >> a >> b;
        map_a_bs[a].emplace_back(b);
        map_a_bs[b].emplace_back(a);
    }

    bfs(sa, da);
    bfs(sb, db);
    bfs(n, dc);  

    if (da[n] == 0x3f3f3f3f || db[n] == 0x3f3f3f3f) { //特判不能达到情况
        cout << -1 << endl;
        return 0;
    }

    //枚举中间结点
    int res = da[n] * wa + db[n] * wb;
    for (int i = 1; i <= n; ++i) {
        if (da[i] == 0x3f3f3f3f || db[i] == 0x3f3f3f3f || dc[i] == 0x3f3f3f3f) {
            continue; //有一个走不到,就跳过
        }
        int ans = da[i] * wa + db[i] * wb + dc[i] * (wa + wb - wc);
        res = min(res, ans);
    }

    cout << res << endl;

    return 0;
}

题目2BD202305糖果促销

C++代码如下,

方法1:二分。注意需要特殊判断“一颗糖纸就能换一颗糖”的情况。

#include<bits/stdc++.h> 

using namespace std;

bool check(int x, int k, int p) {
    int ans = x;
    while (x >= p) {
        ans += x / p;
        x = x / p + x % p;
    }
    return ans >= k;
}

int main( )
{
    int T;
    cin >> T;
    while (T--) {
        int p, k;
        cin >> p >> k;
        if (p == 1) { //特判corner case
            //如果一颗糖纸就能换一颗糖,则购买一颗糖,就可以无限置换,得到无穷多颗糖了
            cout << (bool)k << endl;
            continue;
        }
        int left = 0, right = k;
        int res = -1;
        while (left <= right) {
            int mid = left + right >> 1;
            if (check(mid, k, p)) {
                res = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        cout << res << endl;
    }

    return 0;
}

方法2:数学。

  1. 假设你可以获得k颗糖,其中包括直接购买的糖果和用糖纸兑换而来的糖果。
  2. 那么思考一下,你能获得k颗糖,说明你可以有k张糖纸。而剩余的糖纸为你最后一次兑换的糖果数目,这个值至少为1。因此,你用糖纸兑换而来的糖果数目最大为(k - 1) / p
  3. 那么需要直接购买的糖果数目最少为k - (k - 1) / p
#include <bits/stdc++.h>

using namespace std;

int main() {
    int T, p, k;
    cin >> T;
    while (T--) {
        cin >> p >> k;
        if (k == 0) { //特判一颗糖果都不想吃的情况
            cout << 0 << endl;
            continue;
        }
        int res = k - (k - 1) / p;
        cout << res << endl;
    }
    return 0;
}

题目3BD202311夏日漫步

C++代码如下,

解题思路:BFS。

#include<bits/stdc++.h> 

using namespace std;

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

int main( )
{
    cin >> n;
    unordered_map<int, vector<int>> map_val_idxs;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        map_val_idxs[a[i]].emplace_back(i);
    }

    unordered_map<int, vector<int>> map_a_bs;
    for (auto [_, idxs] : map_val_idxs) {
        for (int i = 0; i < idxs.size() - 1; ++i) {
            int a = idxs[i];
            int b = idxs[i+1];
            map_a_bs[a].emplace_back(b);
        }
    }
    
    for (int i = 1; i <= n; ++i) {
        if (1 <= i-1 && i-1 <= n) {
            map_a_bs[i].emplace_back(i-1);
        }
        if (1 <= i+1 && i+1 <= n) {
            map_a_bs[i].emplace_back(i+1);
        }
    }

    memset(dist, 0x3f, sizeof dist);
    queue<int> q;
    q.push(1);
    dist[1] = 0;
    while (!q.empty()) {
        int a = q.front();
        if (a == n) cout << dist[a] << endl; //特判结果
        q.pop();
        for (auto b : map_a_bs[a]) {
            if (dist[b] == 0x3f3f3f3f) {
                dist[b] = dist[a] + 1;
                q.push(b);
            }
        }
    } 
    
    return 0;
}

题目4BD202314跑步

解题思路:排序。先固定一维,然后再处理另一维。

C++代码如下,

#include<bits/stdc++.h> 

using namespace std;

typedef pair<int,int> PII;
const int N = 1e5 + 10;
PII a[N];
int n;

int main( )
{
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> a[i].first >> a[i].second;
    }

    sort(a, a + n);

    //相同位置处,更新为该位置的最小速度
    for (int i = 0; i < n-1; ++i) {
        if (a[i].first == a[i+1].first) {
            a[i+1].second = a[i].second;
        }
    }

    int res = 1;
    int cur = 1;
    for (int i = n - 1; i >= 0; --i) {
        cur = 1;
        int j = i-1;
        //可能合并成一组小猫的条件:
        //(1)后猫速度大于前猫
        //(2)位置相同
        //满足其一即可
        while (j >= 0 && (a[j].second > a[i].second || a[j].first == a[i].first)) {
            cur += 1;
            j -= 1;
        }
        res = max(res, cur);
        i = j + 1; //更新i
    }

    cout << res << endl;

    return 0;
}

题目5BD202319新的阶乘

解题思路:参照 n ! n! n!中质因数 x x x的数目的求解方法,进行等效变形。可参照acwing算法提高之数学知识–筛质数、分解质因数和快速幂题目4阶乘分解的题解。

C++代码如下,

#include<bits/stdc++.h> 

using namespace std;

int main( )
{
    int n;
    cin >> n;

    //先求解出n以内的质数
    vector<int> primes;
    vector<bool> st(n+1, false);
    for (int i = 2; i <= n; ++i) {
        if (!st[i]) primes.emplace_back(i);
        for (auto prime : primes) {
            if (i * prime > n) break;
            st[i*prime] = true;
            if (i % prime == 0) break;
        }
    }

    printf("f(%d)=", n);

    for (int i = 0; i < primes.size(); ++i) {
        //求f(n)中质因数primes[i]的出现次数,即指数
        long long cnt = 0;
        for (long long x = primes[i]; x <= n; x *= primes[i]) {
            //1,2,3,4,5,...,n这n个数中存在因数x的数为:
            //x, 2x, 3x, ..., n/x*x
            //这些数出现的次数分别为:
            //1+n-x, 1+n-2x, 1+n-3x, ..., 1+n-n/x*x
            long long sval = 1 + n - n / x * x;
            long long eval = 1 + n - x;
            long long cur = ((long long)sval + eval) * (n / x) / 2;  
            cnt += cur;
        }
        if (cnt == 1) {
            printf("%d", primes[i]);
        } else {
            printf("%d^%lld", primes[i], cnt);
        } 

        if (i == primes.size() - 1) {
            printf("\n");
        } else {
            printf("*");
        }
    }

    return 0;
}

题目6BD202321新材料

解题思路:使用哈希表存储上一次出现该数值的下标即可。

C++代码如下,

#include<bits/stdc++.h> 

using namespace std;

const int N = 50010;
int n, k;
int a[N];

int main( )
{
    cin >> n >> k;
    unordered_set<int> targets;
    unordered_map<int, int> map_val_idx;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        if (targets.count(a[i]) != 0) {
            continue; //a[i]已经在目标集中了,不需要判断了
        }
        if (map_val_idx.count(a[i]) != 0) {
            int previ = map_val_idx[a[i]];
            if (i - previ <= k) targets.insert(a[i]);
        } 
        map_val_idx[a[i]] = i;
    }

    int res = 0;
    for (auto x : targets) {
        res ^= x;
    } 

    cout << res << endl;

    return 0;
}

题目7BD202325找矩阵

解题思路:分别考虑S出现在矩形上边界、下边界、左边界、右边界。以S出现在矩形上边界为例,先枚举矩形的下边界,然后再去找到一个符合要求的左边界tj_left和一个符合要求的右边界tj_right

C++代码如下,

#include<bits/stdc++.h> 

using namespace std;

typedef pair<int,int> PII;

const int N = 3e3 + 10;
int n, m;
char g[N][N];
int si, sj;
int s[N][N];

void init() {
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + int(g[i][j] == '#');
        }
    }

    return;
}

int get(int x1, int y1, int x2, int y2) {
    return s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1];
}

int main( )
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> g[i][j];
            if (g[i][j] == 'S') {
                si = i;
                sj = j;
            }
        }
    }

    init();

    vector<PII> rows(n+1, make_pair(0,0));
    for (int i = 1; i <= n; ++i) {
        int left = sj, right = sj;
        while (left >= 1 && g[i][left] != '#') left--;
        left += 1;
        while (right <= m && g[i][right] != '#') right++;
        right -= 1;
        rows[i] = make_pair(left, right);
    }

    //(1)S在矩形上边界
    int up_left = rows[si].first, up_right = rows[si].second; 
    for (int i = si + 1; i <= n; ++i) { //枚举下边界
        int down_left = rows[i].first, down_right = rows[i].second;
        int left = max(up_left, down_left);
        int right = min(up_right, down_right);
        if (left >= right) { //特判退化成一条线的情况
            continue;
        }
        //(si,left~right)
        //(i,left~right)
        //(1.1)在sj的左边找到一个j
        int tj_left = -1;
        for (int j = left; j <= sj; ++j) {
            //(si,j)
            //(i,j)
            if (get(si,j,i,j) == 0) {
                tj_left = j;
                break;
            }
        }
        //(1.2)在sj的右边找到一个j
        int tj_right = -1;
        for (int j = right; j >= sj; --j) {
            //(sj,j)
            //(i,j)
            if (get(si,j,i,j) == 0) {
                tj_right = j;
                break;
            }
        }

        if (tj_left != -1 && tj_right != -1 && tj_left < tj_right) {
            cout << "Yes" << endl;
            return 0;
        }
    }

    //(2)S在矩形下边界
    int down_left = rows[si].first, down_right = rows[si].second;
    for (int i = 1; i <= si-1; ++i) { //枚举上边界
        int up_left = rows[i].first, up_right = rows[i].second;
        int left = max(up_left, down_left);
        int right = min(up_right, down_right);
        if (left >= right) {//特判退化成一条线的情况
            continue;
        }
        //(i,left~right)
        //(si,left~right)
        //(2.1)在sj左边找到一个j
        int tj_left = -1;
        for (int j = left; j <= sj; ++j) {
            //(i,j)
            //(si,j)
            if (get(i,j,si,j) == 0) {
                tj_left = j;
                break;
            }
        }
        //(2.2)在sj右边找到一个j
        int tj_right = -1;
        for (int j = right; j >= sj; --j) {
            //(i,j)
            //(si,j)
            if (get(i,j,si,j) == 0) {
                tj_right = j;
                break;
            }
        }
        if (tj_left != -1 && tj_right != -1 && tj_left < tj_right) {
            cout << "Yes" << endl;
            return 0;
        }
    }

    vector<PII> cols(m+1, make_pair(0,0));
    for (int j = 1; j <= m; ++j) {
        int up = si, down = si;
        while (up >= 1 && g[up][j] != '#') up--;
        up += 1;
        while (down <= n && g[down][j] != '#') down++;
        down -= 1;
        cols[j] = make_pair(up, down);
    }
    
    //(3)S在左边界
    int left_up = cols[sj].first, left_down = cols[sj].second;
    for (int j = sj+1; j <= m; ++j) {//枚举右边界
        int right_up = cols[j].first, right_down = cols[j].second;
        int up = max(left_up, right_up);
        int down = min(left_down, right_down);
        if (up >= down) {//特判
            continue;
        }
        //(up~down,sj)
        //(up~down.j)
        //(3.1)在si上面找到一个i
        int ti_up = -1;
        for (int i = up; i <= si; ++i) {
            //(i,sj),(i,j)
            if (get(i,sj,i,j) == 0) {
                ti_up = i;
                break;
            }
        }
        //(3.2)在si下面找到一个i
        int ti_down = -1;
        for (int i = down; i >= si; --i) {
            //(i,sj),(i,j)
            if (get(i,sj,i,j) == 0) {
                ti_down = i;
                break;
            }
        }
        if (ti_up != -1 && ti_down != -1 && ti_up < ti_down) {
            cout << "Yes" << endl;
            return 0;
        }
    }

    //(4)S在右边界
    int right_up = cols[sj].first, right_down = cols[sj].second;
    for (int j = 1; j <= sj-1; ++j) {//枚举左边界
        int left_up = cols[j].first, left_down = cols[j].second;
        int up = max(left_up, right_up);
        int down = min(left_down, right_down);
        //(up~down,j)
        //(up~down,sj)
        //(4.1)在si上面找到一个i
        int ti_up = -1;
        for (int i = up; i <= si; ++i) {
            //(i,j) (i,sj)
            if (get(i,j,i,sj) == 0) {
                ti_up = i;
                break;
            }
        }
        //(4.2)在si下面找到一个i
        int ti_down = -1;
        for (int i = down; i >= si; --i) {
            //(i,j) (i,sj)
            if (get(i,j,i,sj) == 0) {
                ti_down = i;
                break;
            }
        }
        if (ti_up != -1 && ti_down != -1 && ti_up < ti_down) {
            cout << "Yes" << endl;
            return 0;
        }
    }

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

2 训练-星耀level

题目1BD202302蛋糕划分

C++代码如下,


3 参考

2005年-2023年百度之星题集
【2023百度之星第一场题解】嘉宾:NOI、IOI金牌周航锐
【2023百度之星第二场题解】嘉宾:NOI、IOI金牌周航锐
【2023百度之星第三场题解】嘉宾:NOI、IOI金牌周航锐
【2023百度之星决赛题解】嘉宾:NOI金牌毛嘉怡

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

YMWM_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值