2024
c
c
p
c
中国大学生程序设计竞赛(郑州全国邀请赛)
\Huge{2024ccpc中国大学生程序设计竞赛(郑州全国邀请赛)}2024ccpc中国大学生程序设计竞赛(郑州全国邀请赛) P
r
o
b
l
e
m
s
A
、
B
、
F
、
H
、
J
、
K
、
L
、
M
\huge{Problems~A、B、F、H、J、K、L、M}ProblemsA、B、F、H、J、K、L、M 文章目录 Problem A. Once In My Life 题意 思路 标程 Problem B. 扫雷 1 题意 思路 标程 Problem F. 优秀字符串 题意 思路 标程 Problem H. 随机栈 题意 思路 标程 Problem J. 排列与合数 题意 思路 标程 Problem K. 树上问题 题意 思路 标程 Problem L. Toxel与PCPC II 题意 思路 标程 Problem M. 有效算法 题意 思路 标程 模板 题目链接:Dashboard - 2024 National Invitational of CCPC (Zhengzhou), 2024 CCPC Henan Provincial Collegiate Programming Contest - Codeforces 写在前面… 破铜烂铁选手,这次拿到了邀请赛的铜牌🥉。
再接再励!
补题环节… 题解中的标程只放了伪代码,完整模板我放在了最后。
Problem A. Once In My Life 题意 给定两个整数 n
,
d
n,dn,d ,然后要求构造一个数字 k
kk ,要求 n
×
k
n\times kn×k 的值的数位中包含 0...9
0...90...9 至少一次,并且 d
(
1
≤
d
≤
9
)
d(1\le d\le 9)d(1≤d≤9) 至少两次。
思路 赛时的一道签到题,可是过题数好少。
我们按照顺序来构造即可: 我们考虑先构造出 N
=
n
×
k
N=n \times kN=n×k ,那么 1234567890
+
d
1234567890+d1234567890+d 即符合题意。
然后我们考虑在 N
NN 后面加上若干位数字使得在不改变 N
NN 的前面 10
1010 位的情况下能够被 n
nn 整除。
上一步的具体方法为: 让 N
NN 左移 n
nn 的位数位,然后加上 n
nn (把 n
nn 放在 N
NN 后边),然后减去 N
%
n
N\%nN%n ,就可以被 n
nn 整除了。
标程
#define int long long
void Solved() {
int n, d; cin >> n >> d;
int len = to_string(n).size();
int luck = (1234567890 + d) * pow(10, len);
luck += n;
luck -= luck % n;
cout << luck / n << endl;
}
Problem B. 扫雷 1 题意 进行 n
nn 轮游戏,每轮会获得一个扫雷币,每轮可以买地雷探测器,给出每轮的地雷探测器的价格,求最多能买多少个地雷探测器?
思路 可以维护一个单调队列,每次存这位置地雷探测器的价格和下标。
在单调队列里第 i
ii 个位置下标前攒的扫雷币都可以用这个价格来买。
标程
#define int long long
#define fi first
#define se second
void Solved() {
int n; cin >> n;
vector a;
for(int i = 1; i <= n; i ++ ) {
int x; cin >> x;
while(!a.empty() && a.back().fi >= x) a.pop_back();
a.push_back({x, i});
}
int res = a[0].se / a[0].fi;
int t = a[0].se % a[0].fi, len = a.size();
for(int i = 1; i < len; i ++ ) {
res += (a[i].se - a[i - 1].se + t) / a[i].fi;
t = (a[i].se - a[i - 1].se + t) % a[i].fi;
}
cout << res << endl;
}
Problem F. 优秀字符串 题意 给出优秀字符串的定义: 长度为5。
第三个字符和第五个字符相同。
前四个字符互不相同。
求优秀字符串个数。
思路 签到题,模拟即可。
标程
void Solved() {
int n; cin >> n;
int res = 0;
for(int i = 1; i <= n; i ++ ) {
string s; cin >> s;
if(s.size() != 5) continue;
if(s[2] != s[4]) continue;
bool f = 1;
for(int i = 0; i < 4; i ++ )
for(int j = i + 1; j < 4; j ++ )
if(s[i] == s[j]) f = 0;
res += f;
}
cout << res << endl;
}
Problem H. 随机栈 题意 题目给出 2
n
2n2n 次操作,每次操作有两种情况: −
1
-1−1 :从当前集合中取出一个数。
非 −
1
-1−1 :将当前数字放入集合中。
两种情况各 n
nn 次,求最后取出的数字数组为递增( 小于等于后一项 )的概率,概率 p
q
\frac{p}{q}qp 表示为: p
×
q
−
1
m
o
d
998244353
p\times q^{-1} mod~~998244353p×q−1mod998244353 。
思路 题目要求输出的数字数组为递增,我们可以通过贪心策略每次只取当前集合中最小的数字;如果当前的最小数字小于前面已选择的数字,那么将不可能构造出升序序列,概率为 0
00 。
题中对应的两种操作我们可以通过大根堆和 m
a
p
mapmap 实现。
但是这道题的一个难点是在求概率上: 容易想到,概率中的分子 p
pp 即为当前集合中最小数的个数;分母 q
qq 即为当前集合中的数字个数。
由于概率需要取模,所以需要用到乘法逆元。
在循环模拟的过程中,分子 p
pp 和分母 q
qq 会非常大,但是我们如果在循环中直接求逆元,会超时。
可以在循环过程中将分子分母分别保存,然后在循环外求逆元即可。
标程
const int mod = 998244353;
int quick_mi(int a,int b) {
int ans = 1;
while(b) {
while(b % 2 == 0)
a = a * a % mod, b = b / 2;
ans = ans * a % mod; b = b - 1;
}
return ans ;
}
void solve() {
int n; cin >> n;
for(int i = 1; i<= 2 * n; i++){
cin >> arr[i];
}
priority_queue,greater> que;
int maxx = 0;
vector z, m;
for(int i = 1; i <= 2 * n; i++){
if(arr[i] > -1){
que.push(arr[i]); mp[arr[i]] ++;
} else {
int temp = que.top();
if(temp < maxx){
cout << "0" << endl; return;
}
maxx = max(maxx,temp);
z.push_back(mp[temp]); m.push_back(que.size());
que.pop(); mp[temp]--;
}
}
int ans = 1;
for(int i : z){
ans *= i; ans %= mod;
}
for(int i : m){
ans = ans * quick_mi(i, mod - 2); ans = ans % mod;
}
cout << ans <
Problem J. 排列与合数 题意 给出一个五位整数,然后将其每位重新排列,组成一个合数并输出;如果无法构造,则输出-1。
思路 签到题 构造合数只需将其中的合数位放在最后即可(注意前导零的情况)。
但是如果没有合数的情况呢?
题目样例中已经给出,五位都是奇数的情况直接输出 97531 即可。
所以说没有 −
1
-1−1 的情况,不用考虑。
标程
void Solved() {
string s; cin >> s;
deque dq;
int sum = 0;
for(int i = 0; i < 5; i ++ ) {
int x = s[i] - '0';
if(x & 1) dq.push_front(x), sum ++;
else dq.push_back(x);
}
if(sum == 5) {
cout << "97531\n";
} else {
for(int i : dq) cout << i; cout << endl;
}
}
Problem K. 树上问题 题意 给出一个由 n
nn 各节点组成的无根树,编号为 1...
n
1...n1...n ,每个节点有一个正整数点权a[i]。
现在定义 美丽节点 :如果一个节点作为根节点,当其他所有节点的点权都不小于其父节点点权的 1
2
\frac{1}{2}21 时, 当前根节点为美丽节点 。
思路 考虑从边入手: 若x与y之间有边,那么共有两种情况: a
[
x
]
×
2
<a
[
y
]
