1 介绍
本博客记录百度之星2022编程比赛相关题目。
2 训练–钻石level
题目1:小度养小猫
vip题目,未开放!
题目2:BD202203简单题
解题思路:贪心。
贪心策略:上升序列升得越慢越好,下降序列降得越慢越好。
先去除相邻相等元素的干扰。然后判断,如果当前元素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题目,未开放。
题目6:BD202211逃离这棵树
解题思路:
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}quv∑v∈{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}quv∑v∈{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}quv∑v∈{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
称为b
模mod
的乘法逆元。b
存在乘法逆元的充分必要条件是b
与mod
互质。特别的,如果mod
是质数,那么
x
=
b
m
o
d
−
2
x=b^{mod-2}
x=bmod−2就是b
模mod
的乘法逆元。
因此,将公式(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=a⋅bmod−2 % mod(5)
其中
b
m
o
d
−
2
b^{mod-2}
bmod−2可以使用快速幂来计算。
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;
}
题目7:BD202213星球联通
解题思路:关键是理解c[i]
的含义。题目中说了,可以免费连接任意k
个星球,那么
c[1]
表示连接任意k+1
个星球的花费。c[2]
表示连接任意k+2
个星球的花费。- ……
c[n-k]
表示连接任意n
个星球的花费。
按照题意,总共需要建立n-k
条边,才能够使得这n
个星球连通,它包括了直接建立的边和安装传送门得到的边。也即,
- 当我们直接建立了
0
条边,那么需要通过传送门建立n-k
条边。 - 当我们直接建立了
1
条边,那么需要通过传送门建立n-k-1
条边。 - 当我们直接建立了
2
条边,那么需要通过传送门建立n-k-2
条边。 - ……
- 当我们直接建立了
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;
}
题目8:BD202217不等式组
解题思路: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题目,暂未开放!
题目11:BD202225子序列
解题思路:贪心。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题目,暂未开放!