1 介绍
本博客记录百度之星比赛的题目及解法。
2 训练-钻石level
题目1:BD202301公园
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;
}
题目2:BD202305糖果促销
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:数学。
- 假设你可以获得
k
颗糖,其中包括直接购买的糖果和用糖纸兑换而来的糖果。 - 那么思考一下,你能获得
k
颗糖,说明你可以有k
张糖纸。而剩余的糖纸为你最后一次兑换的糖果数目,这个值至少为1
。因此,你用糖纸兑换而来的糖果数目最大为(k - 1) / p
。 - 那么需要直接购买的糖果数目最少为
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;
}
题目3:BD202311夏日漫步
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;
}
题目4:BD202314跑步
解题思路:排序。先固定一维,然后再处理另一维。
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;
}
题目5:BD202319新的阶乘
解题思路:参照 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;
}
题目6:BD202321新材料
解题思路:使用哈希表存储上一次出现该数值的下标即可。
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;
}
题目7:BD202325找矩阵
解题思路:分别考虑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
题目1:BD202302蛋糕划分
C++代码如下,
3 参考
2005年-2023年百度之星题集
【2023百度之星第一场题解】嘉宾:NOI、IOI金牌周航锐
【2023百度之星第二场题解】嘉宾:NOI、IOI金牌周航锐
【2023百度之星第三场题解】嘉宾:NOI、IOI金牌周航锐
【2023百度之星决赛题解】嘉宾:NOI金牌毛嘉怡