GPLT团体程序设计天梯赛模拟:2025年分组赛

GPLT团体程序设计天梯赛模拟:2025年分组赛

摘要

题目 分值 知识点
P4711 「化学」相对分子质量 - 洛谷 25 字符串处理
P1922 女仆咖啡厅桌游吧 - 洛谷 25 树形DP
B4219 数学作业 - 洛谷 25 DFS剪枝
P1194 买礼物 - 洛谷 25 最小生成树

相对分子质量

字符串模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include<bits/stdc++.h>
using namespace std;

string s;
double sum;
unordered_map<string, double> mp;

// 初始化哈希表
void init_mp(){
mp["H"] = 1;
mp["C"] = 12;
mp["N"] = 14;
mp["O"] = 16;
mp["F"] = 19;
mp["Na"] = 23;
mp["Mg"] = 24;
mp["Al"] = 27;
mp["Si"] = 28;
mp["P"] = 31;
mp["S"] = 32;
mp["Cl"] = 35.5;
mp["K"] = 39;
mp["Ca"] = 40;
mp["Mn"] = 55;
mp["Fe"] = 56;
mp["Cu"] = 64;
mp["Zn"] = 65;
mp["Ag"] = 108;
mp["I"] = 127;
mp["Ba"] = 137;
mp["Hf"] = 178.5;
mp["Pt"] = 195;
mp["Au"] = 197;
mp["Hg"] = 201;
}

// 读取数字,直到i不是数字
int read_num(int & i){
int res = 0;
for(;s[i] >= '0' && s[i] <= '9' ; i++){
res = (s[i] - '0') + res*10;
}
return res;
}

// 读取下一个元素,及其后缀_{n}
double read_elem(int & i){
string t = "";
int num = 1;
if(s[i] >= 'A' && s[i] <= 'Z'){
t += s[i]; i++;
if(i < s.length() && s[i] >= 'a' && s[i] <= 'z'){
t += s[i]; i++;
}
}
if(s[i] == '_'){
i += 2;
num = read_num(i);
i ++;
}
return mp[t]*num;
}

int main(void){
cin>>s;
init_mp();
for(int i = 0 ; i < s.length(); ){
if(s[i] >= 'A' && s[i] <= 'Z'){
sum += read_elem(i);
}else if(s[i] == '('){
double k = 0; i++;
while(s[i] != ')') k += read_elem(i);
i += 3;
sum += k*read_num(i);
i++;
}else if(s[i] == '~'){
i++;
int num = (s[i] == 'H' ? 1: read_num(i));
sum += num*(mp["H"]*2 + mp["O"]);
i += 6;
}else{
i++;
}
}
if(sum == int(sum)) cout<<int(sum);
else cout<<fixed<<setprecision(1)<<sum;
return 0;
}

女仆咖啡厅桌游吧

给一棵树,要对树的节点进行涂色,允许涂两种颜色,要求每个子树的两种颜色数相同,求单一颜色的最大个数。

我们设 红色为1,蓝色为-1,即每个子树的总和为0;

假设节点x的每个子节点已经合法涂色,因为每个子节点代表的子树总和都为0,现在可以考虑的只有叶子节点和父节点。

那么x代表的子树对总和的贡献为 (叶子节点个数 + 1)/ 2。

由此可以得到递推关系: \[ dp[i] = \sum_{j}^{son[i]}dp[j] + cnt/2 \] 这里的son[i]表示非叶子节点,cnt 表示 叶子节点 + 1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;

int n,dp[maxn],d[maxn];
vector<int > e[maxn];

void dfs(int x, int f){
int cnt = 1;
for(auto it : e[x]){
if(it != f){
dfs(it, x);
dp[x] += dp[it];
if(d[it] == 1) cnt++;
}
}
dp[x] += cnt/2;
}

int main(void){
cin>>n;
for(int i = 1 ; i <= n-1 ; i ++){
int u, v; cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
d[v]++;d[u]++;
}
dfs(1, -1);
cout<<dp[1];
return 0;
}

数学作业

给定一个数字n,求 < n 的斐波那契数中,能构成n的组合有几种。

题目给定n的最大值为10^12,斐波那契数最多有58个,而朴素遍历的时间复杂度为O(2^n)。一定会超时。

我们考虑使用前缀和进行优化:

若当前和 + 此后所有数的和 < n,退出循环;

若当前和 > n,退出循环;

注意,对于合法的斐波那契数,我们要从大到小进行遍历,可以极大优化剪枝过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<bits/stdc++.h>
using namespace std;
using ll = long long;

ll n,l,ans;
ll dp[100],pre[100];

void dfs(ll d, ll sum){
if(sum + pre[d] < n) return;
if(sum > n) return;
if(d == 0){
if(sum == n) ans++;
return;
}
dfs(d-1, sum + dp[d]);
dfs(d-1, sum);
}

int main(void){
cin>>n;
dp[0] = dp[1] = 1;
pre[1] = 1;
for(int i = 2 ; dp[i-1] <= n ; i ++){
dp[i] = dp[i-1] + dp[i-2];
pre[i] += pre[i-1] + dp[i];
l = i;
}
dfs(l-1, 0);
cout<<ans;
return 0;
}

买礼物

Kruskal最小生成树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3;

struct edge{
int w;
int x,y;
bool operator<(const edge & rhs){
return w < rhs.w;
}
};
vector<edge> v;

int p, n;
int fa[maxn];

int find(int x){
return fa[x] == x ? x : fa[x] = find(fa[x]);
}

int main(void){
cin>>p>>n;
for(int i = 1 ; i <= n ; i ++) fa[i] = i;
for(int i = 1 ; i <= n ; i ++){
for(int j = 1 ; j <= n ; j ++){
edge e; e.x = i; e.y = j;
cin>>e.w;
if(e.w == 0 || e.w > p) e.w = p;
v.push_back(e);
}
}
sort(v.begin(), v.end());
int cnt = 0;
int sum = 0;
for(int i = 0 ; i < v.size() && cnt < n-1 ; i ++){
int fx = find(v[i].x);
int fy = find(v[i].y);
if(fx == fy) continue;
fa[fx] = fy;
sum += v[i].w;
cnt++;
}
cout<<sum+p;
return 0;
}

GPLT团体程序设计天梯赛模拟:2025年分组赛
https://czwcugb.github.io/题解/GPLT/2025-group-match/
作者
ChenZhiwei
发布于
2025年4月17日
许可协议